通用爬蟲scrapy
一 crawlspider
crawlspider 是scrapy提供的一個通用爬蟲,crawlspider 繼承了spider類,除了擁有spider類的所有方法和屬性。crawlspider還擁有着自己的屬性和方法
- 屬性rules :它定義了爬取網站的規則,是一個包含一個或多個rule的清單,每個rule都對網站的爬取規則做了定義,crawlspider會對rules中的rule進行一個個解析。
- 屬性parse_start_url: 它是一個可以重寫的方法當start_url中的Request的到Response時,它會分析Response并傳回Item對象或是 Response對象
rule參數解析
- link_extractor:是linkExtractor對象,通過它,spider可以知道從爬取頁面中提取哪些連結,提取出的連結自動會生成Request,比較常用
- allow是一個正規表達式或正規表達式清單,定義了符合要求的且需要跟進的連結。allow_domain相當于白名單,裡面的連結符合要求,會生成新的Request,deny_domain相當于黑名單
- restrict_xpath:定義了從目前頁面中XPath比對的區域提取連結,其值是XPath表達式或XPath表達式清單。
- restrict_css定義了從目前頁面中CSS選擇器比對的區域提取連結,其值是CSS選擇器或CSS選擇器清單。還有一些其他參數代表了提取連結的标簽、是否去重、連結的處理等内容,使用的頻率不高。
- callback:即回調函數,和之前定義Request的callback有相同的意義。每次從link_extractor中擷取到連結時,該函數将會調用。該回調函數接收一個response作為其第一個參數,并傳回一個包含Item或Request對象的清單。注意,避免使用parse()作為回調函數。由于CrawlSpider使用**parse()方法來實作其邏輯,如果parse()**方法覆寫了,CrawlSpider将會運作失敗。
- cb_kwargs:字典,它包含傳遞給回調函數的參數。
- follow:布爾值,即True或False,它指定根據該規則從response提取的連結是否需要跟進。如果callback參數為None,follow預設設定為True,否則預設為False。
- process_links:指定處理函數,從link_extractor中擷取到連結清單時,該函數将會調用,它主要用于過濾。
- process_request:同樣是指定處理函數,根據該Rule提取到每個Request時,該函數都會調用,對Request進行處理。該函數必須傳回Request或者None。
二 Itemloader
我們了解了利用CrawlSpider的Rule來定義頁面的爬取邏輯,這是可配置化的一部分内容。但是,Rule并沒有對Item的提取方式做規則定義。對于Item的提取,我們需要借助另一個子產品Item Loader來實作。Item Loader提供一種便捷的機制來幫助我們友善地提取Item。它提供的一系列API可以分析原始資料對Item進行指派。Item提供的是儲存抓取資料的容器,而Item Loader提供的是填充容器的機制。有了它,資料的提取會變得更加規則化。
itemloader 的API參數
- item:它是Item對象,可以調用add_xpath()、add_css()或add_value()等方法來填充Item對象。
- selector:它是Selector對象,用來提取填充資料的選擇器。
- response:它是Response對象,用于使用構造選擇器的Response。
- 另外,Item Loader每個字段中都包含了一個Input Processor(輸入處理器)和一個Output Processor(輸出處理器)。Input Processor收到資料時立刻提取資料,Input Processor的結果被收集起來并且儲存在ItemLoader内,但是不配置設定給Item。收集到所有的資料後,load_item()方法被調用來填充再生成Item對象。在調用時會先調用Output Processor來處理之前收集到的資料,然後再存入Item中,這樣就生成了Item。
内置processor
identity :最簡單的處理,直接傳回資料不做任何處理
Takefirst:TakeFirst傳回清單的第一個非空值,類似extract_first()的功能,常用作Output Processor
Join:Join方法相當于字元串的join()方法,可以把清單拼合成字元串,字元串預設使用空格分隔
Compose是用給定的多個函數的組合而構造的Processor,每個輸入值被傳遞到第一個函數,其輸出再傳遞到第二個函數,依次類推,直到最後一個函數傳回整個處理器的輸出。
mapcompose:與Compose類似,MapCompose可以疊代處理一個清單輸入值
SelectJms:SelectJmes可以查詢JSON,傳入Key,傳回查詢所得的Value。不過需要先安裝Jmespath庫才可以使用它,安裝好Jmespath之後,便可以使用這個Processor了
三本節目标
爬取 中華能源網
在下面貼出代碼及所屬檔案
universal.json
{
“spider”: “universal”,
“website”: “中華能源網”,
“type”: “能源”,
“index”: “http://www.china-nengyuan.com/”,
“settings”: {
“USER_AGENT”: “Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50”
},
“start_urls”: {
“type”: “dynamic”,
“method”: “China_energy”,
“args”: [
5,
10
]
},
“allowed_domains”: [
“www.china-nengyuan.com”
],
“rules”: “China_energy”,
“item”: {
“class”: “FormItem”,
“loader”: “China_energy”,
“attrs”: {
“title”: [
{
“method”: “xpath”,
“args”: [
“//td[@align=‘center’]/h1/text()”
]
}
],
“url”: [
{
“method”: “attr”,
“args”: [
“url”
]
}
],
“text”: [
{
“method”: “xpath”,
“args”: [
“//td[@width=‘75%’]/a//text()”
]
}
],
“inf”: [
{
“method”: “xpath”,
“args”: [
“//td[@width=‘79%’]//text()”
]
}
],
“source”: [
{
“method”: “xpath”,
“args”: [
“//td[@align=‘center’]/strong/a[@class=‘blue’]//text()”
]
}
],
"website": [
{
"method": "value",
"args": [
"中華能源網"
]
}
]
}
}
}
items.py
-- coding: utf-8 --
from scrapy import Field, Item
class FormItem(Item):
title = Field()
text = Field()
inf = Field()
source = Field()
url = Field()
website = Field()
loaders.py
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, Join, Compose
class FormLoader(ItemLoader):
default_output_processor = TakeFirst()
class China_energy(FormLoader):
text_out = Compose(Join(), lambda s: s.strip())//文本輸出處理方法
source_out = Compose(Join(), lambda s: s.strip())//來源輸出處理方法
piplines.py
-- coding: utf-8 --
import codecs,json
//這部分結合了網上提供的執行個體,并不是原本就有的
class scrapyuniversalPipeline(object):
“”"
将資料儲存到json檔案,由于檔案編碼問題太多,這裡用codecs打開,可以避免很多編碼異常問題
在類加載時候自動打開檔案,制定名稱、打開類型(隻讀),編碼
重載process_item,将item寫入json檔案,由于json.dumps處理的是dict,是以這裡要把item轉為dict
為了避免編碼問題,這裡還要把ensure_ascii設定為false,最後将item傳回回去,因為其他類可能要用到
調用spider_closed信号量,當爬蟲關閉時候,關閉檔案
“”"
def init(self):
self.file = codecs.open(‘first.json’, ‘w’, encoding=“utf-8”)
def process_item(self, item, spider):
lines = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(lines)
return item
def spider_closed(self, spider):
self.file.close()
rules.py
//china部分是原作者寫的,我寫的是China_energy部分
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import Rule
rules = {
# ‘china’: (
# Rule(LinkExtractor(allow=‘article/..html’, restrict_xpaths=’//div[@id=“left_side”]//div[@class=“con_item”]’),
# callback=‘parse_item’),
# Rule(LinkExtractor(restrict_xpaths=’//div[@id=“pageStyle”]//a[contains(., “下一頁”)]’))
# ),
‘China_energy’: (
Rule(LinkExtractor(allow='product/..html’,restrict_xpaths=’//table[@class =“martop”]//a[@class=“zixun f14”]’
),
callback=‘parse_item’),
Rule(LinkExtractor(restrict_xpaths=’//table[@class=“membertable_page”]//a[contains(., “下一頁”)]’))
)
}
settings.py
BOT_NAME = ‘scrapyuniversal’
SPIDER_MODULES = [‘scrapyuniversal.spiders’]
NEWSPIDER_MODULE = ‘scrapyuniversal.spiders’
ROBOTSTXT_OBEY = False
ITEM_PIPELINES = {‘scrapyuniversal.pipelines.scrapyuniversalPipeline’: 300,
}
urls.py
def China_energy(start,end):
for page in range(start,end+1):
yield ‘http://www.china-nengyuan.com/product/product_tiny_1068_’ + str(page) + ‘.html’
utils.py
from os.path import realpath, dirname
import json
def 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.py
from scrapy.crawler import CrawlerProcess
def run():
name = sys.argv[1]
custom_settings = get_config(name)
spider = custom_settings.get(‘spider’, ‘universal’)
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()
if name == ‘main’:
run()
讓爬蟲跑起來 效果圖

附上檔案預覽圖
本篇文章根據崔慶才的代碼改寫
附上位址
Scrapy架構的使用之Scrapy通用爬蟲 其中有詳細的講解
https://juejin.im/post/5b026d53518825426b277dd5#heading-8