普通爬虫有啥意思,我写了个通用Scrapy爬虫
大家好,我是Kuls。今天是來自讀者劍南的投稿。
除了錢,大家還比較喜歡什么?當(dāng)然是全能、萬(wàn)能和通用的人或事物啦,例如:全能、什么都會(huì)的員工、萬(wàn)能鑰匙、通用爬蟲等等。今天我們學(xué)習(xí)Scrapy通用爬蟲,利用Scrapy通用爬蟲來獲取美食杰網(wǎng)站[1]。
Scrapy通用爬蟲
創(chuàng)建Scrapy項(xiàng)目
Scrapy爬蟲和Scrapy通用爬蟲都是通過以下執(zhí)行命令來創(chuàng)建Scrapy項(xiàng)目,沒什么不同,命令如下所示:
Scrapy startproject Scrapy項(xiàng)目名Spider爬蟲模板
在創(chuàng)建spider爬蟲前,我們先看看有什么可用的爬蟲模板,執(zhí)行命令如下所示:
scrapy genspider -l運(yùn)行結(jié)果如下圖所示:
其中:
?basic是我們之前創(chuàng)建Spider的時(shí)候,默認(rèn)使用的爬蟲模板,也就是普通的爬蟲模板;?crawl模板是最常用于抓取常規(guī)網(wǎng)站的爬蟲模板,通過指定一些爬取規(guī)則來實(shí)現(xiàn)頁(yè)面的提取,很多情況下這個(gè)模板的爬取就足夠通用;?csvfeed模板是Scrapy最簡(jiǎn)單的爬蟲模板,主要用于解析 CSV 文件,它是以行為單位來進(jìn)行迭代,每迭代一行調(diào)用一次 parse_row() 方法;?xmlfeed模板主要用于處理RSS訂閱信息,RSS是一種信息聚合技術(shù),可以讓信息的發(fā)布和共享更為高效和便捷。
接下來我們主要是講解最常用的爬蟲模板——crawl模板,其他模板我們會(huì)在往后的文章里講解,敬請(qǐng)期待!!!
CrawlSpider
在使用crawl模板前,我們先要了解一下CrawlSpider。
CrawlSpider是Scrapy提供的一個(gè)通用Spider,繼承自Spider類,除了擁有Spider類的所有方法和屬性,它還提供了rules屬性和parse_start_url()方法。
其中:
?rules是包含一個(gè)或多個(gè)Rule對(duì)象的列表,我們可以指定一些爬取規(guī)則來實(shí)現(xiàn)頁(yè)面的提取;?parse_start_url()是一個(gè)可重寫的方法,當(dāng)start_urls里對(duì)應(yīng)的Request得到的Response時(shí),該方法被調(diào)用。
創(chuàng)建crawl模板爬蟲
crawl模板的通用爬蟲通過執(zhí)行以下命令來創(chuàng)建,以http://quotes.toscrape.com網(wǎng)站為例子,該網(wǎng)站是一個(gè)著名作家名言的網(wǎng)站,命令如下所示:
scrapy genspider -t 模板類型 <爬蟲名字> <允許爬取的域名> scrapy genspider -t crawl quotes quotes.toscrape.com當(dāng)然,我們可以把命令中的crawl改為xmlfeed或者csvfeed,這樣就會(huì)生成其他類型的爬蟲,成功創(chuàng)建后,在spiders文件夾中多了一個(gè)quotes.py文件,該文件正是我們創(chuàng)建的spider爬蟲,其內(nèi)容如下所示:
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule class QuotesSpider(CrawlSpider):name = 'quotes'allowed_domains = ['quotes.toscrape.com']start_urls = ['http://quotes.toscrape.com/']rules = (Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),)def parse_item(self, response):item = {}#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()#item['name'] = response.xpath('//div[@id="name"]').get()#item['description'] = response.xpath('//div[@id="description"]').get()return item其中:
?class QuotesSpider()是自定義spider類,繼承自CrawlSpider?name是定義此爬蟲名稱的字符串,每個(gè)項(xiàng)目唯一的名字,用來區(qū)分不同的Spider,啟動(dòng)爬蟲時(shí)使用scrapy crawl +該爬蟲名字;?allowed_domains是允許爬取的域名,防止爬蟲爬到其他網(wǎng)站;?start_urls是最開始爬取的url;?rules是爬取規(guī)則屬性,是一個(gè)包含多個(gè)Rule對(duì)象的列表,該Rule主要用來確定當(dāng)前頁(yè)面中的哪些鏈接需要繼續(xù)爬取、哪些頁(yè)面的爬取結(jié)果需要哪個(gè)方法來解析等。?parse_item()方法是負(fù)責(zé)解析返回響應(yīng)、提取數(shù)據(jù)或進(jìn)一步生成要處理的請(qǐng)求。
注意:不能修改這個(gè)方法的名字,且不能定義parse()方法!!!
在創(chuàng)建Crawl模板的Spider爬蟲時(shí),Rule中只展示了最常用的參數(shù),其完整參數(shù)如下所示:
Rule(LinkExtractor(allow=r'Items/', deny=(), allowed_domains=(), deny_domains=(), restrict_xpaths=()), callback='parse_item', follow=True, cb_kwargs=None, process_links=None, process_request=None)Rule常見的參數(shù)如下:
?LinkExtractor是一個(gè)鏈接提取對(duì)象,它定義了如何從每個(gè)已爬取的頁(yè)面中提取鏈接并用于生成一個(gè)requests對(duì)象;?callback是一個(gè)可調(diào)用對(duì)象或字符,和之前定義requests的callback作用一樣,?指定鏈接提取器提取的每個(gè)鏈接交給哪個(gè)解析函數(shù)去處理;?follow是一個(gè)布爾值,它指定是否從使用此規(guī)則提取的每個(gè)響應(yīng)中跟蹤鏈接,當(dāng)callback為None時(shí),follow默認(rèn)為True,否則為False;?cb_kwargs是字典,其包含了傳遞給回調(diào)用函數(shù)的參數(shù);?process_links指定處理函數(shù),從LinkExtractor中獲取到鏈接列表時(shí),該函數(shù)將會(huì)被調(diào)用,主要用于過濾url;?process_request指定哪個(gè)函數(shù)將會(huì)被調(diào)用,該規(guī)則提取到每個(gè)request時(shí)都會(huì)調(diào)用該函數(shù),主要用于過濾request。
LinkExtractor常用的參數(shù)如下:
?allow:滿足括號(hào)中正則表達(dá)式的URL會(huì)被提取,如果為空,則全部匹配;?deny:滿足括號(hào)中正則表達(dá)式的URL不會(huì)被提取,優(yōu)先級(jí)高于allow;?allow_domains:會(huì)被提取的鏈接的domains;?deny_domains:不會(huì)被提取的鏈接的domains;?restrict_xpaths:使用xpath表達(dá)式來規(guī)則URL地址的范圍。
定義rules規(guī)則
定義rules規(guī)則,也就是確定被提取的URL鏈接及其范圍。
首先我們定義翻頁(yè)的rules規(guī)則,進(jìn)入名人名言網(wǎng)站[2]并打開開發(fā)者工具,如下圖所示:
由圖可知,翻頁(yè)的URL存放在<li class="next"標(biāo)簽中,其下一頁(yè)URL為/page/2/,所以我們可以定義如下rule規(guī)則:
Rule(LinkExtractor(allow=r'/page/\d+', restrict_xpaths='//li[@class="next"]'),follow=True),由于我們?cè)诜?yè)的頁(yè)面中,沒有需要提取的數(shù)據(jù),所以這里沒有callback參數(shù),所以需要加上follow=True。
由圖可以知,<div class="col-md-8"存放著名人名言的數(shù)據(jù)(名字、名言、關(guān)于作者部分信息的URL鏈接等),觀察規(guī)律,發(fā)現(xiàn)每個(gè)a標(biāo)簽中的href是由/author/名字組成,而且名字都是英文字母,所以我們可以設(shè)置如下rules規(guī)則:
rules = (Rule(LinkExtractor(allow=r'/author/\w+',restrict_xpaths='/html/body/div[1]/div[2]/div[1]'), callback='parse_item'), )由于在鏈接提取對(duì)象有我們需要提前的數(shù)據(jù),所以這里需要寫callback參數(shù),不需要寫follow參數(shù)。
定義字段
在提取數(shù)據(jù)之前,我們先在items.py文件中定義字段,具體代碼如下所示:
import scrapy class Test2Item(scrapy.Item):# define the fields for your item here like:name = scrapy.Field()作為演示,我們只定義一個(gè)字段提取作者名,感興趣的小伙伴可以定義多個(gè)字段提取不同的數(shù)據(jù)。
提取數(shù)據(jù)
定義了rules規(guī)則后,我們接下來嘗試在parse_item()方法中提取響應(yīng)的數(shù)據(jù),具體代碼如下所示:
from test2.items import Test2Itemdef parse_item(self, response): item = Test2Item() item['name']=response.xpath('//h3[@class="author-title"]/text()').extract_first() return item首先我們導(dǎo)入Test2Item,實(shí)例化Test2Item,作為演示,我們只提取作者名,感興趣的可以提取其他數(shù)據(jù)。
Item Loader模塊
提取響應(yīng)數(shù)據(jù),我們還可以使用Item Loader模塊,其模塊提供了一種便捷的機(jī)制來幫助我們方便的提取Item數(shù)據(jù),讓我們的數(shù)據(jù)提取變得更加規(guī)則化,其語(yǔ)法規(guī)則為:
變量名=ItemLoader(item={}, response=())變量名.add_選擇器('數(shù)據(jù)字段名', '選擇器規(guī)則')return 變量名.load_item()其中:
?item是對(duì)象;?response是網(wǎng)頁(yè)的響應(yīng)數(shù)據(jù);?add_選擇器:其可以為add_xpath、add_css、add_value()
上面的提取數(shù)據(jù)代碼可以修改為如下代碼,具體代碼如下所示:
from test2.items import Test2Itemfrom scrapy.loader import ItemLoaderdef parse_item(self, response): loader=ItemLoader(item=Test2Item(),response=response) loader.add_xpath('name','//h3[@class="author-title"]/text()') return loader.load_item()首先我們導(dǎo)入Test2Item和ItemLoader模塊,并實(shí)例化ItemLoader和Test2Item,最后通過return loader.load_item()將數(shù)據(jù)返回給引擎。
這種提取方法比較規(guī)則化,我們可以把一些參數(shù)和規(guī)則單獨(dú)提取出來做成配置文件或者存儲(chǔ)到數(shù)據(jù)庫(kù),及可實(shí)現(xiàn)可配置化。
在settings.py文件中啟動(dòng)引擎,并在pipelines.py文件中打印輸出,運(yùn)行結(jié)果如下:
通用配置抽取
有人可能說,就這?就一個(gè)Rule規(guī)則就實(shí)現(xiàn)了通用?等等,別急!!!
在我們爬蟲代碼中,很多代碼都是重復(fù)的,例如變量、方法名幾乎都是一致的,那么我們可以把完全不同的地方抽離出來,做成可配置文件。
我們新建一個(gè)crawl通用爬蟲,執(zhí)行代碼如下所示:
scrapy genspider -t crawl currency quotes.toscrape.com在剛才創(chuàng)建的crawl通用爬蟲中,我們來思考一下哪些數(shù)據(jù)可以抽離出來做成可配置文件?沒錯(cuò),里面所有東西都可以做成配置文件。
配置文件quotes.json
首先我們創(chuàng)建一個(gè)名為configs的文件夾來存放我們的配置文件,然后創(chuàng)建名為quotes.json的文件來把剛才創(chuàng)建的crawl通用爬蟲里面的內(nèi)容都寫入在文件中,具體代碼如下所示:
{ "settings": { "USER_AGENT":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" }, "spider":"currency", "allowed_domains": ["quotes.toscrape.com"], "start_urls": ["http://quotes.toscrape.com/"], "rules": "quotes_rule", "item": { "class": "Test2Item", "loader": "ItemLoader", "attrs": { "name": [ { "method": "xpath", "args": [ "/html/body/div[1]/div[2]/h3/text()" ] } ] } }}首先我們把settings.py文件中的User-Agent配置先寫入到文件中,再把爬蟲名、爬蟲爬取的網(wǎng)站域名、最先爬取的URL鏈接以及rules規(guī)則寫入到文件中,最后把提取數(shù)據(jù)的方法寫入到文件中,其中:
?item:保存抓取數(shù)據(jù)的容器;?class:是我們items.py文件中的類,用來定義數(shù)據(jù)字段;?loader:是填充容器的機(jī)制,也就是上面所講的規(guī)范提取數(shù)據(jù)的ItemLoader模塊;?attrs:表示提取數(shù)據(jù)內(nèi)容;?name:是items.py文件中,定義的字段,也就是我們要提取的作者名字;?method:數(shù)據(jù)提取的方法,我們這里選用了xpath提取;?args:表示提取數(shù)據(jù)的規(guī)則、表達(dá)式;
rules.py規(guī)則文件
有人可能問,rules規(guī)則這么簡(jiǎn)單?當(dāng)然,rules不會(huì)那么簡(jiǎn)單,這里我們新建一個(gè)rules.py文件來存放Rule規(guī)則,具體代碼如下所示:
from scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import Rulerules = { 'quotes_rule':(Rule(LinkExtractor(allow=r'/author/\w+',restrict_xpaths='/html/body/div[1]/div[2]/div[1]'), callback='parse_item'), Rule(LinkExtractor(allow=r'/page/\d+', restrict_xpaths='//li[@class="next"]'),follow=True),)}這里我們把rules規(guī)則已字典的形式來保存,以便我們獲取rules里面的值。
我們創(chuàng)建了配置文件,當(dāng)然要把配置的文件讀取出來了,所以我們新建了一個(gè)名為Read_configs.py的文件來讀取數(shù)據(jù),具體代碼如下所示:
from os.path import realpath,dirnameimport jsondef get_config(name): path = dirname(realpath(__file__)) + '/configs/' + name + '.json' with open(path, 'r', encoding='utf-8')as f: return json.loads(f.read())啟動(dòng)爬蟲run.py
創(chuàng)建讀取文件后,接下來要?jiǎng)?chuàng)建一個(gè)啟動(dòng)Spider爬蟲的文件,我們把它命名為run.py,具體代碼如下所示:
import sysfrom scrapy.utils.project import get_project_settingsfrom test2.Read_configs import get_configfrom scrapy.crawler import CrawlerProcessdef run(): name=sys.argv[1] custom_settings=get_config(name) spider=custom_settings.get('spider','currency') 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()首先我們導(dǎo)入一些模塊和庫(kù),再獲取命令行的參數(shù)并賦值為name,通過剛才在Read_configs.py所創(chuàng)建的get_config()將配置文件quotes.json讀取保存下來,再通過get()方法把Spider爬蟲名獲取下來并存放在spider變量中,通過get_project_settings()方法來獲取Scrapy項(xiàng)目中的settings.py配置并調(diào)用dict()方法把配置變?yōu)樽值涞母袷奖4嬖趕ettings變量中,再調(diào)用update()方法更新custom_settings變量的數(shù)據(jù)內(nèi)容,最后實(shí)例化CrawlerProcess,并調(diào)用crawl()和start()方法啟動(dòng)爬蟲。
spider爬蟲初始化及獲取配置
在啟動(dòng)爬蟲前,首先我們要初始化爬蟲數(shù)據(jù)并通過parse_item()方法獲取屬性配置,具體代碼如下所示:
import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom test2.Read_configs import get_configfrom test2.rules import rulesfrom test2 import nextfrom test2.items import Test2Itemfrom scrapy.loader import ItemLoaderclass CurrencySpider(CrawlSpider): name = 'currency' def __init__(self,name,*args,**kwargs): config=get_config(name) self.config=config self.allowed_domains=config.get('allowed_domains') self.start_urls=config.get('start_urls') self.rules=rules.get(config.get('rules')) super(CurrencySpider, self).__init__(*args,**kwargs) def parse_item(self, response): item=self.config.get('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')) return loader.load_item()首先我們重新定義__init__()方法,把a(bǔ)llowed_domains、start_urls和rules等屬性賦予值,再通過編寫parse_item方法來動(dòng)態(tài)獲取屬性配置從而提取數(shù)據(jù),首先使用get()方法來獲取item配置信息,在使用eval()方法來獲取返回get()中的值。最后通過for循環(huán)來獲取數(shù)據(jù)并返回給引擎。
這里我們的pipeline.py文件只是簡(jiǎn)單地打印數(shù)據(jù),其內(nèi)容如下:
class Test2Pipeline: def process_item(self, item, spider): print(item)最后執(zhí)行以下命令來運(yùn)行爬蟲:
run.py quotes運(yùn)行結(jié)果如下所示:
控制翻頁(yè)數(shù)
那么問題來了,假如翻頁(yè)數(shù)有幾千頁(yè)呢,我們不可能每次都要從第一頁(yè)爬到最后一頁(yè)的吧,怎樣要提取指定頁(yè)面的數(shù)據(jù)呢
這時(shí),我們的start_urls可以在quotes.json文件中改為:
"start_urls": { "type": "dynamic", "method": "next", "args": [ 1,2 ] },其中,type是start_urls類型,method是調(diào)用的方法,args是開始頁(yè)和結(jié)束頁(yè)的頁(yè)碼,大家可以根據(jù)需求來獲取想要的頁(yè)面。
注意把rules.py文件中以下代碼刪除,要不然不能實(shí)現(xiàn)爬取指定頁(yè)數(shù):
Rule(LinkExtractor(allow=r'/page/\d+', restrict_xpaths='//li[@class="next"]'),follow=True),)除了修改start_urls,我們還需要?jiǎng)?chuàng)建實(shí)現(xiàn)method調(diào)用的方法,這里我們上面我們定義的方法是next,所以我們新建一個(gè)next.py文件,其具體代碼為:
def next(start,end): for page in range(start,end+1): yield 'https://www.meishij.net/fenlei/xiafancai/p'+str(page)+'/'再在currency.py文件中加以下代碼來獲取start_urls的值:
from test2 import nextstart_urls=config.get('start_urls')self.start_urls=list(eval('next.'+start_urls.get('method'))(*start_urls.get('args',[])))這樣我們就實(shí)現(xiàn)了指定頁(yè)面的爬取。
這樣,一個(gè)scrapy通用爬蟲就做好了,對(duì)了,為了防止大家弄亂了文件位置,導(dǎo)致程序報(bào)錯(cuò),貼心的我們把項(xiàng)目目錄截圖了下來,如下圖所示:
那么貼心,趕緊轉(zhuǎn)發(fā)、點(diǎn)贊加收藏走一波。
當(dāng)我們想用剛才創(chuàng)建的通用爬蟲時(shí),只要修改quotes.json、next.py、rules.py中的部分代碼即可。
有人可能覺得,我靠,弄一個(gè)Scrapy通用爬蟲要寫那么多.py文件,我還是老老實(shí)實(shí)寫Scrapy普通的爬蟲算了。
接下來我們通過實(shí)戰(zhàn)演練,展示寫了一個(gè)Scrapy通用爬蟲對(duì)以后的網(wǎng)站爬取有多么地方便。
實(shí)戰(zhàn)演練
現(xiàn)在我們來實(shí)戰(zhàn)測(cè)試一下Scrapy通用爬蟲的方便性,測(cè)試的網(wǎng)站為美食杰的下飯菜[3]。
修改rules.py規(guī)則
我們先修改rules規(guī)則:
我們先進(jìn)入美食杰網(wǎng)站并打開開發(fā)者模式,如下圖所示:
由圖可知,<div class="list_s2"存放著我們想要的數(shù)據(jù),而且每個(gè)菜品的具體做法的URL鏈接的規(guī)律也很明顯,都是https://www.meishij.net/zuofa/+菜品拼音+.html,所以我們r(jià)ules.py文件中的rule規(guī)則可以改為如下代碼:
from scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import Rulerules = { 'quotes_rule':(Rule(LinkExtractor(allow=r'https://www.meishij.net/zuofa/\w+\.html',restrict_xpaths='//div[@class="list_s2"]'), callback='parse_item'),)}修改quotes.json配置
點(diǎn)擊具體做法的URL鏈接并打開開發(fā)者模式,如下圖所示:
菜品名存放在<h1 class="recipe_title"中,那么提取菜名的xpath表達(dá)式可以為:
//h1[@class="recipe_title"]/text()那么我們quotes.json文件中的args改為如下代碼:
"attrs": { "name": [ { "method": "xpath", "args": [ "//h1[@class=\"recipe_title\"]/text()" ] } ] }因?yàn)椴煌木W(wǎng)站,其域名也不一樣,所以我們要將域名修改為美食杰的域名,其代碼修改為如下代碼:
"allowed_domains": ["www.meishij.net"],修改next.py翻頁(yè)
首先經(jīng)過簡(jiǎn)單的查找,美食杰的下飯菜前幾頁(yè)的URL鏈接為:
https://www.meishij.net/fenlei/xiafancai/p1/https://www.meishij.net/fenlei/xiafancai/p2/https://www.meishij.net/fenlei/xiafancai/p3/很明顯鏈接最后面的數(shù)字是翻頁(yè)的重要參數(shù),所以我們可以把next.py文件修改為:
def next(start,end): for page in range(start,end+1): yield 'https://www.meishij.net/fenlei/xiafancai/p'+str(page)+'/'好了,全部代碼已經(jīng)修改完畢了。
結(jié)果展示
從結(jié)果上看,我們只是簡(jiǎn)單地修改了Scrapy項(xiàng)目中的一些代碼,就實(shí)現(xiàn)了對(duì)其他網(wǎng)站的數(shù)據(jù)爬蟲,你們懂的,趕緊把文章點(diǎn)贊收藏做一個(gè)Scrapy通用爬蟲來方便自己以后爬取一些簡(jiǎn)單網(wǎng)站的數(shù)據(jù)。
好了,Scrapy通用爬蟲就講解到這里了,感謝觀看!!!
引用鏈接
[1]?美食杰網(wǎng)站:?https://www.meishij.net/
[2]?名人名言網(wǎng)站:?https://quotes.toscrape.com/
[3]?美食杰的下飯菜:?https://www.meishij.net/fenlei/xiafancai/
總結(jié)
以上是生活随笔為你收集整理的普通爬虫有啥意思,我写了个通用Scrapy爬虫的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在穷学生面前 “ 摆弄骚姿 ”,最美90
- 下一篇: 全球最优秀的14位程序员