當前位置:
首頁 > 知識 > Python分散式爬蟲詳解(二)

Python分散式爬蟲詳解(二)


程序員大咖

點擊右側關注,免費進階高級!




作者:

Zhiqi Kou


zhihu.com/people/zhiqi-kou


本章知識點:


a.CrawlSpider爬取電影天堂動作片第一頁數據


b.將項目改為RedisCrawlSpider爬蟲


一、CrawlSpider爬蟲


要想搭建分散式爬蟲,我們可以寫一個普通的爬蟲,然後對其修改,使之成為分散式爬蟲。


首先,新建

CrawlSpider

項目:

scrapy

 

genspider

 

-t

 

crawl

 

dytt_slaver

 

dy2018

.com



1、定義爬取欄位

import

 scrapy

class

 

DyttRedisSlaverItem(scrapy.Item)

:


    

# 譯名


    name = scrapy.Field()
    

# 年代


    year = scrapy.Field()
    

# 語言


    language = scrapy.Field()
    

# 上映日期


    release_date = scrapy.Field()
    

# 評分

    score = scrapy.Field()
    

# 文件大小


    file_size = scrapy.Field()
    

# 片長


    film_time = scrapy.Field()
    

# 簡介


    introduction = scrapy.Field()
    

# 海報


    posters = scrapy.Field()
    

# 下載鏈接


    download_link = scrapy.Field()

2、定義Rule規則


查看網頁源碼發現,電影鏈接為

/i/[一串數字].html

的形式,但是我們只需要類目中的電影而不需要推薦的電影:



所以:

movie_links = LinkExtractor(allow=

r"/i/d*.html"

, restrict_xpaths=(

"//div[@class="co_content8"]"

))

    rules = (
        Rule(movie_links, callback=

"parse_item"

),
    )

3、定義提取影片信息規則


觀察網頁源碼,發現頁面結構並不是統一的:




有的信息在p標籤中包裹,但是也有一些信息在div標籤中。而且,由於主演人數不同,標籤個數也不確定。所以,用xpath進行提取不是那麼的方便了。這種情況就需要選用正則表達式來進行篩選。


觀察網頁編碼,為

gb2312



有些小眾的電影並沒有評分。


所以,篩選規則為:

        str_resp = response.body.decode(

"gb2312"

, errors=

"ignore"

)
        rep_chars = [

" "

"·"

"“"

"”"

"…"

]
        

for

 rep 

in

 rep_chars:
            str_resp = str_resp.replace(rep, 

""

)

        title = re.search(

r"◎片  名(.*?)</.+>"

, str_resp).group(

1

).replace(

u" "

""

)
        translation = re.search(

r"◎譯  名(.*?)</.+>"

, str_resp).group(

1

).replace(

u" "

""

)
        

# 名字


        items[

"name"

] = title + 

"|"

 + translation
        

# 年代


        items[

"year"

] = re.search(

r"◎年  代(.*?)</.+>"

, str_resp).group(

1

).replace(

u" "

""

)
        

# 評分


        

try

:
            items[

"score"

] = response.xpath(

"//strong[@class="rank"]/text()"

).extract()[

0

].replace(

u" "

""

)
        

except

:
            items[

"score"

] = 

"無評分"


            

# 語言


        items[

"language"

] = re.search(

r"◎語  言(.*?)</.+>"

, str_resp).group(

1

).replace(

u" "

""

)
        

# 上映日期


        items[

"release_date"

] = re.search(

r"◎上映日期(.*?)</.+>"

, str_resp).group(

1

).replace(

u" "

""

)
        

# 文件大小


        items[

"file_size"

] = re.search(

r"◎文件大小(.*?)</.+>"

, str_resp).group(

1

).replace(

u" "

""

)
        

# 片長


        items[

"film_time"

] = re.search(

r"◎片  長(.*?)</.+>"

, str_resp).group(

1

).replace(

u" "

""

)
        

# 簡介


        items[

"introduction"

] = re.search(

r"◎簡  介</.+><.+>(.*?)</.+>"

, str_resp).group(

1

).replace(

u" "

""

)
        

# 海報


        items[

"posters"

] = response.xpath(

"//div[@id="Zoom"]/*[1]/img/@src"

).extract()[

0

]

經測試發現,網站的迅雷下載鏈接是用js動態生成的。這就要用到selenium了。

from

 selenium 

import

 webdriver

# 下載鏈接


items[

"download_link"

] = self.get_download_link(response.url)

def

 

get_download_link(self, url)

:


        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_argument(

"--headless"

)
        chrome_options.add_argument(

"--disable-gpu"

)
        driver = webdriver.Chrome(chrome_options=chrome_options)
        driver.get(url)
        link = re.search(

r""(thunder:.*?)""

,  driver.page_source).group(

1

)
        driver.close()
        

return

 link

最後,pipelines中保存數據:

class

 

DyttRedisSlaverPipeline

(

object

):


    

def

 

__init__(

self

)

:
        

self

.file = open(

"movie.json"

"w"

)

    

def

 

process_item(

self

, item, spider)

:
        content = json.dumps(dict(item), ensure_ascii=False) + 

""


        

self

.file.write(content)
        

return

 item

    

def

 

close_spider(

self

, spider)

:
        

self

.file.close()

運行爬蟲,得到第一頁的30條數據:



二、修改項目為RedisCrawlSpider爬蟲


1、首先修改爬蟲文件


① RedisCrawlSpider修改很簡單,首先需要引入RedisCrawlSpider:

from

 scrapy_redis.spiders 

import

 RedisCrawlSpider

② 將父類中繼承的

CrawlSpider

改為繼承

RedisCrawlSpider

class

 

DyttSlaverSpider(RedisCrawlSpider)

:



③ 因為slaver端要從redis資料庫中獲取爬取的鏈接信息,所以去掉

allowed_domains()

 和 

start_urls

,並添加

redis_key

redis_key

 = 

"dytt:start_urls"



④ 增加

__init__()

方法,動態獲取

allowed_domains()

,[理論上要加這個,但是實測加了爬取的時候鏈接都被過濾了,所以我沒加,暫時沒發現有什麼影響]

     

def

 

__init__(

self

, *args, **kwargs)

:
         domain = kwargs.pop(

"domain"

""

)
         

self

.allowed_domains = filter(None, domain.split(

","

))
         

super

(DyttSlaverSpider, 

self

).__init_

_

(*args, **kwargs)

2、修改setting文件


① 首先要指定redis資料庫的連接參數:

REDIS_HOST

 = 

"192.168.0.131"


REDIS_PORT

 = 

6379



② 指定使用

scrapy-redis

的調度器

SCHEDULER

 = 

"scrapy_redis.scheduler.Scheduler"



③ 指定使用

scrapy-redis

的去重

DUPEFILTER_CLASS

 = 

"scrapy_redis.dupefilter.RFPDupeFilter"



④ 指定排序爬取地址時使用的隊列

# 默認的 按優先順序排序(Scrapy默認),由sorted set實現的一種非FIFO、LIFO方式。


SCHEDULER_QUEUE_CLASS

 = 

"scrapy_redis.queue.SpiderPriorityQueue"


# 可選的 按先進先出排序(FIFO)


# SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"


# 可選的 按後進先出排序(LIFO)


# SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"



⑤ 設置斷點續傳,也就是不清理redis queues

SCHEDULER_PERSIST

 = 

True



⑥ 默認情況下,

RFPDupeFilter

只記錄第一個重複請求。將

DUPEFILTER_DEBUG

設置為

True

會記錄所有重複的請求。

DUPEFILTER_DEBUG

 =

True



⑦ 配置

RedisPipeline

item

寫入

key

為 

spider.name : items

 的redis的list中,供後面的分散式處理item

ITEM_PIPELINES = {
   

"dytt_redis_slaver.pipelines.DyttRedisSlaverPipeline"

: 300,
   

"scrapy_redis.pipelines.RedisPipeline"

: 400
}

3、增加爬蟲信息欄位(可選)


由於會有多個slaver端,所以可加一個爬蟲名字的欄位和時間欄位來區分是哪個爬蟲在什麼時間爬到的信息。


① item中增加欄位

    

# utc時間


    

crawled

 = scrapy.Field()
    

# 爬蟲名


    spider = scrapy.Field()

② pipelines中新增類:

class

 

InfoPipeline(object)

:

    

def

 

process_item(self, item, spider)

:


        

#utcnow() 是獲取UTC時間


        item[

"crawled"

] = datetime.utcnow()
        

# 爬蟲名


        item[

"spider"

] = spider.name
        

return

 item

③ setting中設置ITEM_PIPELINES

ITEM_PIPELINES = {
   

"dytt_redis_slaver.pipelines.DyttRedisSlaverPipeline"

: 300,
   

"dytt_redis_slaver.pipelines.InfoPipeline"

:350,
   

"scrapy_redis.pipelines.RedisPipeline"

: 400
}

至此,項目修改完畢,現在可以爬取某一分類下的第一頁的電影信息。


以Windows10為slaver端運行一下:



因為請求隊列為空,所以爬蟲會停下來進行監聽,直到我們在Master端給它一個新的連接:



爬蟲啟動,開始爬取信息:



爬取完成後,項目不會結束,而是繼續等待新的爬取請求的到來,爬取結果:



本章小結:


本章將一個crawlspider爬蟲改為了RedisCrawlSpider爬蟲,可以實現分散式爬蟲,但是由於數據量較小(只有30條)所以只用了一個slaver端。並且沒有去設置代理ip和user-agent,下一章中,針對上述問題,將對項目進行更深一步的修改。


項目源碼:

https:

//github.com/ZhiqiKou/Scrapy_notes




【點擊成為源碼大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

告訴你三個抗衰老的方法

TAG:Python開發 |