天天看點

通用爬蟲 crawlspider 多站點爬取

通用爬蟲scrapy

一 crawlspider

crawlspider 是scrapy提供的一個通用爬蟲,crawlspider 繼承了spider類,除了擁有spider類的所有方法和屬性。crawlspider還擁有着自己的屬性和方法

  1. 屬性rules :它定義了爬取網站的規則,是一個包含一個或多個rule的清單,每個rule都對網站的爬取規則做了定義,crawlspider會對rules中的rule進行一個個解析。
  2. 屬性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()

讓爬蟲跑起來 效果圖

通用爬蟲 crawlspider 多站點爬取

附上檔案預覽圖

通用爬蟲 crawlspider 多站點爬取

本篇文章根據崔慶才的代碼改寫

附上位址

Scrapy架構的使用之Scrapy通用爬蟲 其中有詳細的講解

https://juejin.im/post/5b026d53518825426b277dd5#heading-8