Scrapy糗事百科爬蟲實戰代碼分析
視頻教學網址:【python爬蟲_從入門到精通(高級篇)】scrapy框架、反爬、分佈式爬蟲
一、Scrapy糗事百科之爬取單頁數據並保存
具體的創建方法可以參照上一篇文章Python最火爬蟲框架Scrapy入門與實踐
創建的目錄結構如下:
在qsbk_spider.py文件中,response是一個’scrapy.http.response.html.HtmlResponse’對象.可以執行’xpath’和’css’語法來提取數據。
# -*- coding: utf-8 -*-
import scrapy
from qsbk.items import QsbkItem
class QsbkSpiderSpider(scrapy.Spider):
name = 'qsbk_spider'
allowed_domains = ['qiushibaike.com']
start_urls = ['https://www.qiushibaike.com/text/page/1/']
def parse(self, response):
# SelectorList
duanzidivs = response.xpath("//div[@class='col1 old-style-col1']/div")
# 遍歷div
for duanzidiv in duanzidivs:
# Selector-->Unicode
author = duanzidiv.xpath(".//a/h2/text()").get().strip()
content = duanzidiv.xpath(".//div[@class='content']//text()").getall()
content = " ".join(content).strip()
# 提取出來的數據,是一個’Selector’或者是一個’SelectorList’對象。如果想要獲取其中的字符串,那麼應該執行’getall’或是’get’方法
# getall方法:獲取Selector中的所有文本,返回的是一個列表。get方法:是獲取selector中的第一個文本,返回的是str類型.
item = QsbkItem(author=author,content=content)
#如果數據解析回來,要傳給pipline處理.那麼可以使用’yield’來返.或是可以收集到所有的item,最後統一使用return返回.
yield item
在itmes.py文件中,用來存放爬蟲爬取下來的數據類型,裏面的數據與qsbk_spider.py 定義的 一 一對應。
import scrapy
# item:建議在item.py中定義好模型.以後就不要使用字典了.在最後將數據變成字典就行了
class QsbkItem(scrapy.Item):
author = scrapy.Field()
content = scrapy.Field()
在piplines文件中,用來將items的模型存儲到本地磁盤中;
pipeline:這個是專門用來保存數據地.其中三個方法是會經常用到的
①open_spider(self,spider):當爬蟲被打開的時候就會被調用
②process_item(self,item,spider):當爬蟲有item傳過來的時候就會被調用
③close_spider(self,spider):當爬蟲關閉的時候會被調用
要激活piplilne , 應該在settings.py中設置ITEM_PIPELINES將其激活.
import json
class QsbkPipeline(object):
def __init__(self):
self.fp = open("duanzi.json",'w',encoding="utf-8") # 創建一個導入的對象
def open_spider(self,spider):
print("爬蟲開始了。。")
def process_item(self, item, spider):
# 將item轉換爲字典,再將字典轉換爲json ;item是爬蟲返回的數據
item_json = json.dumps(dict(item),ensure_ascii=False)
# json字符串寫入文件當中
self.fp.write(item_json+'\n')
return item
def close_spider(self,spider):
self.fp.close()
print("爬蟲結束了。。")
settings.py: 本爬蟲的一些配置信息(比如請求頭、多久發送一次、ip代理池等等),需要將下面的註釋去掉。
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36 SLBrowser/6.0.1.4221'
}
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'qsbk.pipelines.QsbkPipeline': 300, #值越小優先級越高;當設置多個爬蟲時,可以參考此值。
}
由於每一次在命令端調試比較麻煩,可以在qsbk文件的根目錄創建start.py文件進行調試。
from scrapy import cmdline
# 調式
cmdline.execute("scrapy crawl qsbk_spider".split())
# cmdline.execute(['scrapy', 'crawl', 'qsbk_spider'])
二、Scrapy糗事百科之優化數據存儲方式
在pipelines.py文件裏保存json數據的時候,可以使用這兩個類,讓操作更加簡單。
①JsonItemExporter:這個是每次把數據添加到內存中.最後統一寫入到磁盤中.
好處就是存儲的數據是一個滿足json規則的數據.
壞處就是如果數據量比較大,那麼比較消耗內存
from scrapy.exporters import JsonItemExporter
class QsbkPipeline(object):
def __init__(self):
# 二進制打開文件,因爲JsonItemExporter是以二進制寫入寫入的
self.fp = open("duanzi.json",'wb')
self.exporter = JsonItemExporter(self.fp,ensure_ascii=False,encoding="utf-8")
# 開始導入
self.exporter.start_exporting()
def open_spider(self,spider):
print("爬蟲開始了。。")
def process_item(self, item, spider):
# 先把傳進來的字典塞到一個列表當中
self.exporter.export_item(item)
return item
def close_spider(self,spider):
# 完成導入
self.exporter.finish_exporting() # 再統一寫道json文件中
json文件存儲一行數據:
[{"author": "山鷹寂寞飛", "content": "那年那人那山第三十二章:錢去樓空 紅軍很驚訝的問:“怎麼了?是怪我昨天沒來接你嗎?”秀芬面無表情,眼角淚珠往下飛速滾淌着。 紅軍湊上前,蹲下握住秀芬的手道:“好了,我知道錯了,下次不管再晚,哪怕騎車也過來接你,好不好?” 秀芬觸電一般用力拽回自己的手,歇斯底里大叫一句:“你走啊!我不想再看到你!” 紅軍嚇了一跳,見秀芬如此激動,好像真想分手,有點莫名其妙,他盯了秀芬一會,眼見她側臉昂頭,只流淚不再說話,態度相當堅決,思忖一會問:“爲什麼?總得給我個理由吧?是…因爲我沒\n…\n \n\n 查看全文"}]......
②JsonLinesItemExporter:這個是每次調用export_item的時候就吧這個item存儲到硬盤當中.
壞處是每一個字典是一行,整個文件不是一個滿足json格式的文件.
好處就是每次處理數據的時候就直接存儲到硬盤當中,這樣不會消耗內存,數據也比較安全.
from scrapy.exporters import JsonLinesItemExporter
class QsbkPipeline(object):
def __init__(self):
# 二進制打開文件,因爲JsonItemExporter是以二進制寫入寫入的
self.fp = open("duanzi.json",'wb')
self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding="utf-8")
def open_spider(self,spider):
print("爬蟲開始了。。")
def process_item(self, item, spider):
# 先把傳進來的字典塞到一個列表當中
self.exporter.export_item(item)
return item
def close_spider(self,spider):
self.fp.close()
print("爬蟲結束了。。")
輸出結果:一個字典一行的存儲:
{"author": "山鷹寂寞飛", "content": "那年那人那山第三十三章:煉獄化療 傳說中的化療很快來到,秀芬和紅軍以爲是什麼工程浩大驚天動地的療法,沒成想也就是幾瓶藥水輸進身體。 然而他們都小瞧了這幾瓶藥水的威力,剛掛上沒一會,楚建國開始噁心,說胃裏火燒火燎像有硫酸在腐蝕,不停的乾嘔着,後來實在憋持不住,讓秀芬趕緊拿來垃圾桶,他側倒在牀沿翻江倒海的抽搐着身子,一陣又一陣吐得眼淚直流。 早上吃的東西全都吐了個乾淨,順着嘴角又一佝僂一佝僂嘔出黃疸水,偌大的一個壯漢,竟然像個孩子似的,吐的哭出了聲音。 秀芬很害怕,去找值班醫生,\n…\n \n\n 查看全文"}
{"author": "五少爺的刀", "content": "中午在食堂吃午飯的時候,剛好和廠長坐在一起,爲了活躍氣氛,我就講了兩個笑話。 廠長果然被我逗笑了,噗~的一聲,把他剛扒進嘴裏的飯全噴在我碗裏了。。。 大家都放下了碗筷,扭頭看我。 就在我在考慮是不是不要了的時間,廠長帶的二哈跳起來把我的飯吃了!…… 謝天謝地啊,謝天謝地!!!……我站起來,朝着二哈,一躬到地。。。"}
{"author": "精靈不次飯~", "content": "穿了條新裙子,照鏡子覺得特別好看。我腦子一抽就發視頻給在玩遊戲的老公,問他好看不,還跟他賣個萌做了個鬼臉。 現在我很想把他踹出家門!!這貨不停的追問我“你直接喊我來看不就行了嗎?”“我在家呢,你發視頻幹啥?”“你是不是發錯人了?” “你本來打算髮給誰的?”"}
.......
三、Scrapy糗事百科之抓取多個頁面
修改qsbk_spider.py,分析"下一頁"按鈕的標籤結構
# -*- coding: utf-8 -*-
import scrapy
from qsbk.items import QsbkItem
class QsbkSpiderSpider(scrapy.Spider):
name = 'qsbk_spider'
allowed_domains = ['qiushibaike.com']
start_urls = ['https://www.qiushibaike.com/text/page/1/']
base_domain = "https://www.qiushibaike.com"
def parse(self, response):
# SelectorList
duanzidivs = response.xpath("//div[@class='col1 old-style-col1']/div")
# 遍歷div
for duanzidiv in duanzidivs:
# Selector-->
author = duanzidiv.xpath(".//a/h2/text()").get().strip()
content = duanzidiv.xpath(".//div[@class='content']//text()").getall()
content = " ".join(content).strip()
item = QsbkItem(author=author,content=content)
yield item
next_url = response.xpath("//ul[@class='pagination']/li[last()]/a/@href").get()
if not next_url:
return
else:
yield scrapy.Request(self.base_domain+next_url,callback=self.parse)
# 返回當前的請求
在setting文件中,找到下面的註釋掉,可以調成一秒獲取一次數據
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 1