開頭再來波小程序搖一搖:php
上一節,咱們說到了爬取普通頁面的每一條item怎麼搞。相信你們以及對 CrawlSpider 裏面的 Rule 有所瞭解。css
接下來,今天咱們就作一個完結。寫一個通用的CrawlSpider。html
爲啥更新的這麼慢?由於正則表達式
以前教程拿來作試驗的網站是「每天美劇」,http://www.ttmeiju.me/, 可是鬼知道,特麼的寫了兩篇文章以後,因爲不可抗拒力,每天美劇被下線了。臥了個槽,這豈不是以前寫的全白搭了嗎?萌新小白想學習爬蟲,看了代碼,可是例子跑步起來,這尼瑪怎麼搞?學習體驗及其差的好伐啦。shell
那麼今天的內容,其實仍是和之前是銜接的起來的,只不過咱們換了一個網站而已,並且這個網站更加的有趣,工整。地址我就不說了,代碼裏面都有,文章主要是着重講解一下寫一個靈活爬蟲的關鍵點。數據庫
因爲以前的每天美劇掛了,因此咱們更新了一個更加有意思的網站來做爲爬蟲爬取的對象。具體的網址就不說了,感興趣的同窗能夠在代碼裏面看到。代碼的獲取方式:關注公號『皮爺擼碼』,回覆『代碼』便可找到,記住對應的代碼編號是『CS001』,代碼編號是『CS001』,代碼編號是『CS001』。編程
這個網站呢,大概就是張這個樣子:json
咱們作的就是要爬取這個網站。小程序
爲了方便學習交流,咱們打算先來拿某類別的做品集合做爲 start_url 。好比(域名並不是正確):bash
http://jxxpop.com/idol
好比這個地址,裏面就是寫真集合。
其實,作這些東西,咱們只須要寫出來兩個 Rule 就能夠,一個是 下一頁的Rule
, 另外一個則是每個 item 的 Rule
。
怎麼找這兩個Rule的xpath?我在上一篇文章中就講到了,能夠經過 scrapy shell 來作。我這裏就再也不重複,找到以後,結果以下:
rules = ( Rule(LinkExtractor(allow='.*\.html', restrict_xpaths='//div[@class="entry"]//li/a[1]'), callback='parse_item'), Rule(LinkExtractor(restrict_xpaths='//div[@class="wp-pagenavi"]/a[contains(.,"Next")]')), )複製代碼
上面的兩個Rule,第一個對應的是尋找每個Item的Rule,看到他有個parse_item的回調函數, 第二個則是下一頁的Rule。
接下來就是針對每個Item怎麼解析的問題了。
咱們首先在 items.py
文件裏面聲明咱們的item類:
class JxxpopItem(scrapy.Item): video_url = scrapy.Field() video_title = scrapy.Field() video_num = scrapy.Field() video_img_poster = scrapy.Field() video_img_screenshot = scrapy.Field() video_tags = scrapy.Field()複製代碼
能夠看到,總共有這麼六個變量。這留個變量就是咱們要在parse_item()函數裏面解析出來的。接下來看咱們在每個item頁面是如何解析出來頁面中這些變量的。
尋找每個變量的xpath路徑,一樣是用到 Scrapy shell 來尋找。廢話很少說,咱們來看 Parse_item() 方法長什麼樣子:
def parse_item(self, response): item = JxxpopItem() page_url = response.url page_title = response.xpath('//div[@class="box-b"]/h1/text()').extract_first() re_pattern = '\[.*\]' page_num_temp_list = re.findall(re_pattern, page_title) if page_num_temp_list: page_num = page_num_temp_list[0][1:-1] page_img_poster = response.xpath('//div[@class="box-b"]/div[@class="entry"]/p[@class="poster"]/img/@src').extract() page_img_screenshot = response.xpath('//div[@class="box-b"]/div[@class="entry"]/p[@class="screenshot"]/img/@src').extract() page_tag_list = response.xpath('//div[@class="box-b"]/div[@class="entry"]/div[@class="post-meta"]/div/p/a/text()').extract() item['video_title'] = page_title item['video_url'] = page_url item['video_num'] = page_num item['video_img_poster'] = page_img_poster item['video_img_screenshot'] = page_img_screenshot item['video_tags'] = page_tag_list print(item)複製代碼
這裏看到,爲了尋找這幾個變量,這裏運用到的知識點比較多,既有xpath讀取屬性,又有正則表達式匹配。最後經過print(item)
只是把記過打印出來,若是想要讓item走後面的流程,須要經過 yield item
來將 JxxpopItem 拋出來。咱們運行一下結果看一下:
恩,結果都打印出來了,很完美。
想要經過一個JSON文件來控制咱們的爬蟲,首先在工程裏面建立一個configs
文件夾,裏面放置咱們的配置文件,同時,咱們還得有一個 utils.py
文件,裏面提供咱們讀取配置文件的方法,
#utils.pydef get_config(name): path = dirname(realpath(__file__)) + '/configs/' + name + '.json' with open(path, 'r', encoding='utf-8') as f: return json.loads(f.read())複製代碼
同時,咱們須要將咱們的啓動程序文件也要修改一下:
#Run.pydef run_spider_by_json(): name = sys.argv[1] custom_settings = get_config(name) spider = custom_settings.get('spider', 'jxxpop') project_settings = get_project_settings() settings = dict(project_settings.copy()) settings.update(custom_settings.get('settings')) process = CrawlerProcess(settings) process.crawl(spider, **{'name': name}) process.start()複製代碼
接着,纔是咱們的重頭戲,修改 spider 文件。這裏,咱們須要作這麼幾件事情。首先改寫 __init__()
方法,咱們要在這個地方,將json 文件中的配置信息讀取到 spider 裏面,接着就是重寫 parse_item() 方法,裏面須要加入 itemLoader。這樣才能更爽的來定製爬蟲。
def __init__(self, name, *args, **kwargs): config = get_config(name) self.config = config self.rules = rules.get(config.get('rules')) start_urls = config.get('start_urls') if start_urls: if start_urls.get(j'type') == 'static': self.start_urls = start_urls.get('value') elif start_urls.get('type') == 'datetime': self.start_urls = list(eval('urls.' + start_urls.get('name'))(start_urls.get('date'))) self.allowed_domains = config.get('allowed_domains') super(JxxpopSpider, self).__init__(*args, **kwargs) def parse_item(self, response): item = self.config.get('item') if item: cls = eval(item.get('class'))() loader = eval(item.get('loader'))(cls, response=response) # 動態獲取屬性配置 for key, value in item.get('attrs').items(): for extractor in value: if extractor.get('method') == 'xpath': loader.add_xpath(key, *extractor.get('args'), **{'re': extractor.get('re')}) if extractor.get('method') == 'css': loader.add_css(key, *extractor.get('args'), **{'re': extractor.get('re')}) if extractor.get('method') == 'value': loader.add_value(key, *extractor.get('args'), **{'re': extractor.get('re')}) if extractor.get('method') == 'attr': loader.add_value(key, getattr(response, *extractor.get('args'))) # yield loader.load_item() print(loader.load_item())複製代碼
這裏可能比較晦澀,可是結合以前的 parse_item() 版本,和咱們的配置文件:
"start_urls": { "type": "datetime", "name": "jxxpop_url", "value": ["http://jxxpop.com/category/censored"], "date":"20190529" }, "allowed_domains": [ "jxxpop.com" ], "rules": "jxxpop", "item": { "class": "JxxpopItem", "loader": "JxxpopLoader", "attrs": { "video_url": [ { "method": "attr", "args": [ "url" ] } ], "video_title": [ { "method": "xpath", "args": [ "//div[@class='box-b']/h1/text()" ] } ], "video_num": [ { "method": "xpath", "args": [ "//div[@class='box-b']/h1/text()" ], "re": "\\[.*\\]" } ], "video_img_poster": [ { "method": "xpath", "args": [ "//div[@class='box-b']/div[@class='entry']/p[@class='poster']/img/@src" ] } ], "video_img_screenshot": [ { "method": "xpath", "args": [ "//div[@class='box-b']/div[@class='entry']/p[@class='screenshot']/img/@src" ] } ], "video_tags": [ { "method": "xpath", "args": [ "//div[@class='box-b']/div[@class='entry']/div[@class='post-meta']/div/p/a/text()" ] } ] } }複製代碼
你就能理解的差很少了。每個變量,都對應着他在程序裏面的解析方法,從配置文件裏面讀取出來,而後交給 loader 處理。
#itemloader.pyclass JxxpopLoader(ItemLoader): video_title_out = TakeFirst() video_url_out = TakeFirst() video_num_out = Compose(get_video_num) video_img_poster_out = Join()複製代碼
這裏面主要是涉及到一些 in
和 out
的處理。這裏的TakeFirst()
則是取第一個; Compose(fun)
則是能夠經過括號裏面的函數來對結果作處理;Join()
則是將列表整合成一個。
能夠看到配置文件裏面,咱們這裏要爬取的是5月29日的發佈結果,運行看一下結果:
完美。
爲啥要這麼搞,主要緣由就是爲了方便。由於爬蟲和反爬,一直就是道和魔之間的鬥爭,你今天寫好了爬蟲,各類 xpath 都寫死了,並且爬蟲也部署了,結果明天,網站的頁面結構發生變化了,你這豈不是還得改代碼,而後從新發版?
可是若是採用經過配置文件來配置爬蟲的方法,那麼你須要作的,只是修改配置文件就能夠了。這樣能夠在最大程度上削減發版的次數,並且效率還很高,也不用再次全面測試。
目前這個爬蟲,只是把數據爬取的部分完成了。後續的步驟還有:將結果存入數據庫,爬蟲部署到服務器上。這兩個步驟,我以前都寫過。因此這個系列的文章,主要就是爲了來讓你們瞭解一下,怎樣可以寫出來一個能夠經過靈活配置就能工做的爬蟲。
由於文章都是涉及到服務器的,因此福利就要寫在最前面:
過大年了,你們是否是又有了壓歲錢了啊??啊哈哈哈哈,壓歲錢買糖吃還不如投資到本身身上。好比用來買課程,或者用來買服務器,來學習編程,寫爬蟲。來買服務器啊買服務器啊!只在本地跑,根本沒用的!恰巧,鏟屎官這裏就有上千元的阿里雲和騰訊雲的優惠券給你使用(每一款優惠只要點擊優惠連接,進入便可領取):阿里雲部分:
【阿里雲新人1888元雲產品通用代金券】:
promotion.aliyun.com/ntms/yunpar…【阿里雲爆款雲主機,2折優惠券】:
promotion.aliyun.com/ntms/act/qw…【阿里雲企業級服務器2折優惠券】:
promotion.aliyun.com/ntms/act/en…騰訊雲:
【新客戶無門檻領取總價值高達2775元代金券,每種代金券限量500張,先到先得】:
cloud.tencent.com/redirect.ph…
代碼我已經在公衆號裏面分享了,想要獲取源碼的同窗,能夠關注公號『皮爺擼碼』,回覆『代碼』便可找到,記住對應的代碼編號是『CS001』,代碼編號是『CS001』,代碼編號是『CS001』。