Scrapy爬蟲進階操做之CrawlSpider(三)血崩啊

開頭再來波小程序搖一搖:php

上一節,咱們說到了爬取普通頁面的每一條item怎麼搞。相信你們以及對 CrawlSpider 裏面的 Rule 有所瞭解。css

接下來,今天咱們就作一個完結。寫一個通用的CrawlSpider。html

爲啥更新的這麼慢?由於正則表達式

血崩
啊! 血崩啊! 是血崩啊!!

以前教程拿來作試驗的網站是「每天美劇」,http://www.ttmeiju.me/, 可是鬼知道,特麼的寫了兩篇文章以後,因爲不可抗拒力,每天美劇被下線了。臥了個槽,這豈不是以前寫的全白搭了嗎?萌新小白想學習爬蟲,看了代碼,可是例子跑步起來,這尼瑪怎麼搞?學習體驗及其差的好伐啦。shell

那麼今天的內容,其實仍是和之前是銜接的起來的,只不過咱們換了一個網站而已,並且這個網站更加的有趣,工整。地址我就不說了,代碼裏面都有,文章主要是着重講解一下寫一個靈活爬蟲的關鍵點。數據庫

0x00_這是個啥網站

因爲以前的每天美劇掛了,因此咱們更新了一個更加有意思的網站來做爲爬蟲爬取的對象。具體的網址就不說了,感興趣的同窗能夠在代碼裏面看到。代碼的獲取方式:關注公號『皮爺擼碼』,回覆『代碼』便可找到,記住對應的代碼編號是『CS001』,代碼編號是『CS001』,代碼編號是『CS001』。編程

這個網站呢,大概就是張這個樣子:json

不可描述
不可描述

咱們作的就是要爬取這個網站。小程序

0x01_都有些啥

爲了方便學習交流,咱們打算先來拿某類別的做品集合做爲 start_url 。好比(域名並不是正確):bash

http://jxxpop.com/idol

好比這個地址,裏面就是寫真集合。

0x02_Rule

其實,作這些東西,咱們只須要寫出來兩個 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。

0x03_Parse_item()

接下來就是針對每個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 拋出來。咱們運行一下結果看一下:

不可描述,反正就是成功了
不可描述,反正就是成功了

恩,結果都打印出來了,很完美。

0x05_如何才能動態配置呢?

想要經過一個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()複製代碼

這裏面主要是涉及到一些 inout 的處理。這裏的TakeFirst() 則是取第一個; Compose(fun) 則是能夠經過括號裏面的函數來對結果作處理;Join() 則是將列表整合成一個。

能夠看到配置文件裏面,咱們這裏要爬取的是5月29日的發佈結果,運行看一下結果:

不可描述,反正就是成功了
不可描述,反正就是成功了

完美。

0x06_爲啥要這麼作

爲啥要這麼搞,主要緣由就是爲了方便。由於爬蟲和反爬,一直就是道和魔之間的鬥爭,你今天寫好了爬蟲,各類 xpath 都寫死了,並且爬蟲也部署了,結果明天,網站的頁面結構發生變化了,你這豈不是還得改代碼,而後從新發版?

可是若是採用經過配置文件來配置爬蟲的方法,那麼你須要作的,只是修改配置文件就能夠了。這樣能夠在最大程度上削減發版的次數,並且效率還很高,也不用再次全面測試。

0x07_尾巴

目前這個爬蟲,只是把數據爬取的部分完成了。後續的步驟還有:將結果存入數據庫,爬蟲部署到服務器上。這兩個步驟,我以前都寫過。因此這個系列的文章,主要就是爲了來讓你們瞭解一下,怎樣可以寫出來一個能夠經過靈活配置就能工做的爬蟲。

由於文章都是涉及到服務器的,因此福利就要寫在最前面
過大年了,你們是否是又有了壓歲錢了啊??啊哈哈哈哈,壓歲錢買糖吃還不如投資到本身身上。好比用來買課程,或者用來買服務器,來學習編程,寫爬蟲。來買服務器啊買服務器啊!只在本地跑,根本沒用的!恰巧,鏟屎官這裏就有上千元的阿里雲和騰訊雲的優惠券給你使用(每一款優惠只要點擊優惠連接,進入便可領取):

阿里雲部分
【阿里雲新人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…

【騰訊雲服務器、雲數據庫特惠,3折優惠券】:
cloud.tencent.com/redirect.ph…

0x08_獲取方式

代碼我已經在公衆號裏面分享了,想要獲取源碼的同窗,能夠關注公號『皮爺擼碼』,回覆『代碼』便可找到,記住對應的代碼編號是『CS001』,代碼編號是『CS001』,代碼編號是『CS001』。


相關文章
相關標籤/搜索