在爬蟲課堂(二十二)|使用LinkExtractor提取鏈接中講解了LinkExtractor的使用,本章節(jié)來講解使用CrawlSpider+LinkExtractor+Rule進(jìn)行全站爬取苫昌。
一颤绕、CrawlSpider介紹
Scrapy框架中分兩類爬蟲,Spider類和CrawlSpider類祟身。Spider類的使用已經(jīng)講解了很多奥务,但是如果想爬取某個(gè)網(wǎng)站的全站信息的話,CrawlSpider類是個(gè)非常不錯(cuò)的選擇袜硫。CrawlSpider繼承于Spider類氯葬,CrawlSpider是爬取那些具有一定規(guī)則網(wǎng)站的常用爬蟲,可以說它是為全站爬取而生父款。
它除了繼承過來的屬性(name溢谤、allow_domains)外,還提供了新的屬性和方法:
1.1憨攒、LinkExtractors
class scrapy.linkextractors.LinkExtractor
Link Extractors 的目的很簡(jiǎn)單:提取鏈接世杀。每個(gè)Link Extractor有唯一的公共方法是 extract_links(),它接收一個(gè) Response 對(duì)象肝集,并返回一個(gè) scrapy.link.Link 對(duì)象瞻坝。
Link Extractors要實(shí)例化一次,并且 extract_links()方法會(huì)根據(jù)不同的 Response 調(diào)用多次提取鏈接?
主要參數(shù)如下:
- allow:滿足括號(hào)中“正則表達(dá)式”的值會(huì)被提取杏瞻,如果為空所刀,則全部匹配衙荐。
- deny:與這個(gè)正則表達(dá)式(或正則表達(dá)式列表)不匹配的URL一定不提取。
- allow_domains:會(huì)被提取的鏈接的domains浮创。
- deny_domains:一定不會(huì)被提取鏈接的domains忧吟。
- restrict_xpaths:使用XPath表達(dá)式,和allow共同作用過濾鏈接斩披。
關(guān)于Link Extractors如何使用可以參考爬蟲課堂(二十二)|使用LinkExtractor提取鏈接這篇文章溜族。
1.2、rules
在rules中包含一個(gè)或多個(gè)Rule對(duì)象垦沉,每個(gè)Rule對(duì)爬取網(wǎng)站的動(dòng)作定義了特定操作煌抒。如果多個(gè)Rule匹配了相同的鏈接,則根據(jù)規(guī)則在本集合中被定義的順序厕倍,第一個(gè)會(huì)被使用寡壮。
Rule類的定義如下:
class scrapy.contrib.spiders.
Rule
(link_extractor,callback=None,cb_kwargs=None,follow=None,process_links=None,process_request=None)
主要參數(shù)如下:
- link_extractor:是一個(gè)Link Extractor對(duì)象。其定義了如何從爬取到的頁面提取鏈接讹弯。
- callback:是一個(gè)callable或string(該Spider中同名的函數(shù)將會(huì)被調(diào)用)况既。從link_extractor中每獲取到鏈接時(shí)將會(huì)調(diào)用該函數(shù)。該回調(diào)函數(shù)接收一個(gè)response作為其第一個(gè)參數(shù)闸婴,并返回一個(gè)包含Item以及Request對(duì)象(或者這兩者的子類)的列表坏挠。
- cb_kwargs:包含傳遞給回調(diào)函數(shù)的參數(shù)(keyword argument)的字典芍躏。
- follow:是一個(gè)boolean值邪乍,指定了根據(jù)該規(guī)則從response提取的鏈接是否需要跟進(jìn)。如果callback為None对竣,follow默認(rèn)設(shè)置True庇楞,否則默認(rèn)False。
- process_links:是一個(gè)callable或string(該Spider中同名的函數(shù)將會(huì)被調(diào)用)否纬。從link_extrator中獲取到鏈接列表時(shí)將會(huì)調(diào)用該函數(shù)吕晌。該方法主要是用來過濾。
- process_request:是一個(gè)callable或string(該spider中同名的函數(shù)都將會(huì)被調(diào)用)临燃。該規(guī)則提取到的每個(gè)request時(shí)都會(huì)調(diào)用該函數(shù)睛驳。該函數(shù)必須返回一個(gè)request或者None。用來過濾request膜廊。
二乏沸、CrawlSpider使用
假設(shè)我們要爬取簡(jiǎn)書的所有用戶的信息(用戶名稱、關(guān)注數(shù)爪瓜、粉絲數(shù)蹬跃、文章數(shù)、字?jǐn)?shù)铆铆、收獲喜歡數(shù))蝶缀,如下圖25-1所示的用戶主頁:
用戶的主頁地址為http://www.reibang.com/u/c34455009dd8
2.1丹喻、定義Item文件
from scrapy.item import Item, Field
# 簡(jiǎn)書的全站用戶信息
class JianshuUserItem(Item):
# 用戶名稱
name = Field()
# 關(guān)注數(shù)
followNumber = Field()
# 粉絲數(shù)
fansNumber = Field()
# 文章數(shù)
articleNumber = Field()
# 字?jǐn)?shù)
wordCount = Field()
# 收獲喜歡數(shù)
likeNumber = Field()
2.2、定義pipeline文件
import json
# 設(shè)置字符集翁都,防止編碼參數(shù)出錯(cuò)
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
from scrapy.exporters import JsonItemExporter
class DataSubmitJsonFilePipeline(object):
def __init__(self):
self.file = open('jianshu.json', 'wb')
# 把item寫入JSON文件
def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\n"
self.file.write(line)
return item
def close_spider(self, spider):
self.file.close()
2.3碍论、定義setting文件
ITEM_PIPELINES = {
'tutorial.pipelines.DataSubmitJsonFilePipeline': 1,
}
2.4、編寫spider文件
首先需要找出用戶個(gè)人主頁鏈接的通用字符柄慰,從http://www.reibang.com/u/c34455009dd8
及更多的其他用戶個(gè)人主頁URL分析得知通用字符為/u/
骑冗。
# response中提取鏈接的匹配規(guī)則,得出符合條件的鏈接
pattern = '.*/u/*.'
pagelink = LinkExtractor(allow=pattern)
分析個(gè)人主頁的HTML代碼先煎,得知用戶的用戶名稱贼涩、關(guān)注數(shù)、粉絲數(shù)薯蝎、文章數(shù)遥倦、字?jǐn)?shù)、收獲喜歡數(shù)等字段都是在//div[@class='main-top']
中占锯,如下圖25-2所示:
進(jìn)一步分析HTML袒哥,如下圖25-3所示:
編寫提取用戶名稱、關(guān)注數(shù)消略、粉絲數(shù)堡称、文章數(shù)、字?jǐn)?shù)艺演、收獲喜歡數(shù)等值的代碼如下:
# 用戶名稱
item['name'] = each.xpath("./div[@class='title']/a/text()").extract()[0]
# 關(guān)注數(shù)
item['followNumber'] = each.xpath("./div[@class='info']/ul/li[1]//a/p/text()").extract()[0]
# 粉絲數(shù)
item['fansNumber'] = each.xpath("./div[@class='info']/ul/li[2]//a/p/text()").extract()[0]
# 文章數(shù)
item['articleNumber'] = each.xpath("./div[@class='info']/ul/li[3]//a/p/text()").extract()[0]
# 字?jǐn)?shù)
item['wordCount'] = each.xpath("./div[@class='info']/ul/li[4]//p/text()").extract()[0]
# 收獲喜歡數(shù)
item['likeNumber'] = each.xpath("./div[@class='info']/ul/li[5]//p/text()").extract()[0]
最后完整代碼如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# **********************************************************
# * Author : huangtao
# * Email : huangtao@yimian.me
# * Create time : 2018/4/1 下午6:34
# * Last modified : 2018/4/1 下午6:34
# * Filename : jianshu_spider_crawl.py
# * Description :
# **********************************************************
# 導(dǎo)入鏈接匹配規(guī)則類却紧,用來提取符合規(guī)則的鏈接
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from tutorial.items import JianshuUserItem
class JianshuCrawl(CrawlSpider):
name = "jianshu_spider_crawl"
# 可選,加上會(huì)有一個(gè)爬取的范圍
allowed_domains = ["jianshu.com"]
start_urls = ['http://www.reibang.com/']
# response中提取鏈接的匹配規(guī)則胎撤,得出符合條件的鏈接
pattern = '.*jianshu.com/u/*.'
pagelink = LinkExtractor(allow=pattern)
# 可以寫多個(gè)rule規(guī)則
rules = [
# 只要符合匹配規(guī)則晓殊,在rule中都會(huì)發(fā)送請(qǐng)求,同時(shí)調(diào)用回調(diào)函數(shù)處理響應(yīng)伤提。
# rule就是批量處理請(qǐng)求巫俺。
Rule(pagelink, callback='parse_item', follow=True),
]
# 不能寫parse方法,因?yàn)樵创a中已經(jīng)有了肿男,會(huì)覆蓋導(dǎo)致程序不能跑
def parse_item(self, response):
for each in response.xpath("http://div[@class='main-top']"):
item = JianshuUserItem()
# 用戶名稱
item['name'] = each.xpath("./div[@class='title']/a/text()").extract()[0]
# 關(guān)注數(shù)
item['followNumber'] = each.xpath("./div[@class='info']/ul/li[1]//a/p/text()").extract()[0]
# 粉絲數(shù)
item['fansNumber'] = each.xpath("./div[@class='info']/ul/li[2]//a/p/text()").extract()[0]
# 文章數(shù)
item['articleNumber'] = each.xpath("./div[@class='info']/ul/li[3]//a/p/text()").extract()[0]
# 字?jǐn)?shù)
item['wordCount'] = each.xpath("./div[@class='info']/ul/li[4]//p/text()").extract()[0]
# 收獲喜歡數(shù)
item['likeNumber'] = each.xpath("./div[@class='info']/ul/li[5]//p/text()").extract()[0]
# 把數(shù)據(jù)交給管道文件
yield item