在之前我簡單的實現了 Scrapy的基本內容。
存在兩個問題需要解決。
- 先爬取詳情頁面,在根據頁面url獲取圖片太費事了,要進行簡化,一個項目就實現圖片爬取。
- 增量爬蟲,網站數據更新,獲取更新內容。
一般爬蟲的邏輯是:給定起始頁面,發起訪問,分析頁面包含的所有其他鏈接,然後將這些鏈接放入隊列,再逐次訪問這些隊列,直至邊界條件結束。爲了針對列表頁+詳情頁這種模式,需要對鏈接抽取(link extractor)的邏輯進行限定。我們先了解一下 crawlspider rules。
crawlspider rules的運行機制
request url的獲取是Rule定位到的容器裏,所有a標籤裏的href鏈接,比如用xpath定位。
rules = (
Rule(LinkExtractor(restrict_xpaths=("//table[@class="tbspan"]/tr/td/b/a[2]")),callback='parse_item'),
)
LinkExtractor 構造器各參數說明:https://www.cnblogs.com/xinglejun/p/10408630.html
容器下的所有a標籤的鏈接全都會被獲取到,並且侍自動發送請求,不用自己手動發送請求。響應頁面是Rule請求的鏈接的頁面內容,也就是你定位容器ul裏的a標籤的鏈接,然後你用鼠標點擊該鏈接所看到的頁面內容,在callback函數裏提取數據也是在這個頁面裏提取,parse_item(self,response),這個函數的response對象就是這個頁面內容,千萬不要搞錯對象了。
follow,follow就是跟進的意思,如果follow=True,那麼,在你獲取到的響應頁面裏,所以符合Rule規則的href鏈接都會被獲取到,而且它還會自動跟進,進入到獲取到的鏈接響應頁面,在該頁面又一次匹配Rule規則,看看有沒有符合的,如果有又繼續跟進,一直到匹配不到規則爲止。
詳細的解釋看:https://blog.csdn.net/joe8910/article/details/85159059。
rules = (
Rule(LinkExtractor(restrict_xpaths=("//table[@class="tbspan"]/tr/td/b/a[2]")),callback='parse_item'),
Rule(LinkExtractor(allow=r'list_4_\d+\.html'), callback='parse_item', follow=True),
)
一條是獲取響應頁面規則,一條是獲取翻頁鏈接規則。我們運行一下試試。
class ChinesemedicineSpider(CrawlSpider):
name = 'chinesemedicine'
#allowed_domains = ['http://yzs.satcm.gov.cn']
start_urls = ['https://www.ygdy8.net/html/gndy/china/index.html']
rules = (
Rule(LinkExtractor(restrict_xpaths=("//table[@class='tbspan']/tr/td/b/a[2]")),callback='parse_item'),
Rule(LinkExtractor(allow=r'list_4_\d+\.html'), follow=True),
)
def parse_item(self, response):
item = {}
print('page: %s' % response.url)
這個是一個方法實現,但我們不選擇這個,而選擇另一個辦法實現。
class ChinesemedicineSpider(CrawlSpider):
name = 'chinesemedicine'
#allowed_domains = ['http://yzs.satcm.gov.cn']
start_urls = ['https://www.ygdy8.net/html/gndy/china/index.html']
rules = (
Rule(LinkExtractor(allow=r'list_4_\d+\.html'),callback='parse_item', follow=True),
)
def parse_item(self, response):
detail_url_list = ['https://www.ygdy8.net' + el for el in response.xpath( "//table[@class='tbspan']/tr/td/b/a[2]/@href").extract()]
for url in detail_url_list:
yield scrapy.Request(url=url, callback=self.parse_detail)
def parse_detail(self,response):
print('url :'+response.xpath('//p/img[1]/@src').extract_first())
他與上一個類似,但區別在於使用 parse_detail(self,response)來解析詳情頁面,這個好處在於我們後面在增量查詢時,減少建立連接數。
增量式爬取工作
- 在發送請求之前判斷這個URL之前是不是爬取過
- 在解析內容之後判斷該內容之前是否爬取過
- 在寫入存儲介質時判斷內容是不是在該介質中
去重的方法
將爬取過程中產生的URL進行存儲,存入到redis中的set中,當下次再爬取的時候,對在存儲的URL中的set中進行判斷,如果URL存在則不發起請求,否則就發起請求。
對爬取到的網站內容進行唯一的標識,然後將該唯一標識存儲到redis的set中,當下次再爬取數據的時候,在進行持久化存儲之前,要判斷該數據的唯一標識在不在redis中的set中,如果在,則不在進行存儲,否則就存儲該內容。
一般而言,對url的去重是增量爬蟲的關鍵,所以必須去重。爬蟲爬取的是大規模的相同類型網站,對於每一個爬蟲的結果還會彙總,所以對於內容解析去重並不一定要做,我們可以在彙總後寫入存儲介質時再去重,但對於彙總時對傳輸有要求時,還是解析內容去重。
Redis 安裝
Window 下安裝
下載地址:https://github.com/tporadowski/redis/releases。
Redis 支持 32 位和 64 位。這個需要根據你係統平臺的實際情況選擇,這裏我們下載 Redis-x64-xxx.zip壓縮包到 C 盤,解壓後,將文件夾重新命名爲 redis。
打開一個 cmd 窗口 使用 cd 命令切換目錄到 C:\redis 運行:
redis-server.exe redis.windows.conf
如果想方便的話,可以把 redis 的路徑加到系統的環境變量裏,這樣就省得再輸路徑了,後面的那個 redis.windows.conf 可以省略,如果省略,會啓用默認的。
這時候另啓一個 cmd 窗口,原來的不要關閉,不然就無法訪問服務端了。
切換到 redis 目錄下運行:
redis-cli.exe -h 127.0.0.1 -p 6379
這樣就可以在shell中操作redis了。
Redis 的 Set 是 String 類型的無序集合。集合成員是唯一的,這就意味着集合中不能出現重複的數據。
Redis 中集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是 O(1)。
我們把爬取過的url添加到Redis的set中,這樣就可以通過判斷url是否在set中判斷url是否爬取過。
具體代碼如下:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from redis import Redis
class ChinesemedicineSpider(CrawlSpider):
name = 'chinesemedicine'
start_urls = ['https://www.ygdy8.net/html/gndy/china/index.html']
rules = (
Rule(LinkExtractor(allow=r'list_4_\d+\.html'),callback='parse_item', follow=True),
)
def parse_item(self, response):
conn = Redis(host='127.0.0.1', port=6379)
detail_url_list = ['https://www.ygdy8.net' + el for el in response.xpath(
"//table[@class='tbspan']/tr/td/b/a[2]/@href").extract()]
for url in detail_url_list:
# ex == 1:set中沒有存儲url
print("from :"+ url)
ex = conn.sadd('movies_url',url)
if ex == 1:
yield scrapy.Request(url=url, callback=self.parse_detail)
else:
print('已爬取過')
def parse_detail(self,response):
#item = Scrapy2Item()
print('url :'+response.xpath('//p/img[1]/@src').extract_first())
#yield item
這樣我們實現了簡單的增量爬蟲。