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