Python分散式爬蟲詳解(二)
程序員大咖
點擊右側關注,免費進階高級!
作者:
Zhiqi Kou
zhihu.com/people/zhiqi-kou
本章知識點:
a.CrawlSpider爬取電影天堂動作片第一頁數據
b.將項目改為RedisCrawlSpider爬蟲
一、CrawlSpider爬蟲
要想搭建分散式爬蟲,我們可以寫一個普通的爬蟲,然後對其修改,使之成為分散式爬蟲。
首先,新建
CrawlSpider
項目:
scrapy genspider -t crawl dytt_slaver dy2018 .com
1、定義爬取欄位
import
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
的形式,但是我們只需要類目中的電影而不需要推薦的電影:
所以:
r"/i/d*.html" "//div[@class="co_content8"]"movie_links = LinkExtractor(allow=
rules = (
Rule(movie_links, callback=
"parse_item"
),)
3、定義提取影片信息規則
觀察網頁源碼,發現頁面結構並不是統一的:
有的信息在p標籤中包裹,但是也有一些信息在div標籤中。而且,由於主演人數不同,標籤個數也不確定。所以,用xpath進行提取不是那麼的方便了。這種情況就需要選用正則表達式來進行篩選。
觀察網頁編碼,為
gb2312
有些小眾的電影並沒有評分。
所以,篩選規則為:
"gb2312" "ignore" " " "·" "“" "”" "…" for in "" str_resp = response.body.decode(
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 import
# 下載鏈接
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 import
② 將父類中繼承的
CrawlSpider
改為繼承
RedisCrawlSpider
:
class DyttSlaverSpider(RedisCrawlSpider)
③ 因為slaver端要從redis資料庫中獲取爬取的鏈接信息,所以去掉
allowed_domains()
和
start_urls
,並添加
redis_key
redis_key "dytt:start_urls"
④ 增加
__init__()
方法,動態獲取
allowed_domains()
,[理論上要加這個,但是實測加了爬取的時候鏈接都被過濾了,所以我沒加,暫時沒發現有什麼影響]
def __init__( self
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
"dytt_redis_slaver.pipelines.DyttRedisSlaverPipeline" "scrapy_redis.pipelines.RedisPipeline"ITEM_PIPELINES = {
}
3、增加爬蟲信息欄位(可選)
由於會有多個slaver端,所以可加一個爬蟲名字的欄位和時間欄位來區分是哪個爬蟲在什麼時間爬到的信息。
① item中增加欄位
# utc時間 crawled # 爬蟲名
spider = scrapy.Field()
② pipelines中新增類:
class InfoPipeline(object)
def
process_item(self, item, spider)
:#utcnow() 是獲取UTC時間
item[
"crawled"
] = datetime.utcnow()# 爬蟲名
item[
"spider"
] = spider.namereturn
item③ setting中設置ITEM_PIPELINES
"dytt_redis_slaver.pipelines.DyttRedisSlaverPipeline" "dytt_redis_slaver.pipelines.InfoPipeline" "scrapy_redis.pipelines.RedisPipeline"ITEM_PIPELINES = {
}
至此,項目修改完畢,現在可以爬取某一分類下的第一頁的電影信息。
以Windows10為slaver端運行一下:
因為請求隊列為空,所以爬蟲會停下來進行監聽,直到我們在Master端給它一個新的連接:
爬蟲啟動,開始爬取信息:
爬取完成後,項目不會結束,而是繼續等待新的爬取請求的到來,爬取結果:
本章小結:
本章將一個crawlspider爬蟲改為了RedisCrawlSpider爬蟲,可以實現分散式爬蟲,但是由於數據量較小(只有30條)所以只用了一個slaver端。並且沒有去設置代理ip和user-agent,下一章中,針對上述問題,將對項目進行更深一步的修改。
項目源碼:
//github.com/ZhiqiKou/Scrapy_noteshttps:
【點擊成為源碼大神】
TAG:Python開發 |