1. 程式人生 > 實用技巧 >python的scrapy框架使用例項(以糗事百科為例)和xpath的使用

python的scrapy框架使用例項(以糗事百科為例)和xpath的使用

這篇部落格主要是講一下scrapy框架的使用,對於糗事百科爬取資料並未去專門處理

最後爬取的資料儲存為json格式

一、先說一下pyharm怎麼去看一些函式在原始碼中的程式碼實現

按著ctrl然後點選函式就行了

先給出專案的目錄:

二、先說一下setting.py檔案中一些變數的含義

BOT_NAME = 'qsbk'
# 定義一下這個專案的根
# 以後想要把這個專案某一個檔案中的某個內容匯入到其他檔案,就可以以“qsbk.檔名”來實現
# 例如:
# from qsbk.items import QsbkItem
# 這個意思就是將qsbk目錄下的items.py檔案中的QsbkItem類匯入到此檔案中

# 如果它的值為True,那麼網站上沒有爬蟲協議,那麼程式就會什麼都不執行就停止執行
ROBOTSTXT_OBEY = False

DOWNLOAD_DELAY = 3 # 下載延遲開啟,一方面防止ip被封,另一方面也不要給網站伺服器太大壓力
# 設定預設請求頭
DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
  'user-agent': ''
}
ITEM_PIPELINES = {
   
'qsbk.pipelines.QsbkPipeline': 300, # 通過管道輸出資料,不解除這個註釋,就不會執行pipelines.py一部分程式碼 # 值越小,優先順序越高 }

二、start.py檔案

因為每次執行scrapy都需要在命令列下執行,用慣了pycharm之後看著不太習慣,所以這個檔案就是代替在命令列下輸入執行爬蟲命令,它的執行結果也會在pycharm上顯示,這樣就不用每次開啟cmd去執行scrapy的爬蟲程式了

start.py內容:

from scrapy import cmdline
# qsbk_spider是你的爬蟲程式檔案,注意要把這個命令用split拆開 cmdline.execute("scrapy crawl qsbk_spider".split())

三、Item.py檔案內容

import scrapy


class QsbkItem(scrapy.Item):
    # 這個是固定寫法,也就是給QsbkItem類定義兩個變數
    # Item是爬蟲爬取到資料後,傳遞給管道去儲存資料的單位。傳遞給管道pipelines.py的Item也可以是字典形式
    # 可以自己定義字典,最後返回這個字典也可以,(返回什麼資料可以根據自己喜好,你也可以返回列表)
    author = scrapy.Field()
    content = scrapy.Field()

四、pipelines.py檔案

我們使用scrapy內建類來完成匯出資料,可見這個類可以自動把item變成字典,所以我們就不用去多處理

同時在這個檔案中有三個函式,其中兩個open_spider和close_spider檔案中沒有給我們定義,我們需要自己定義。這兩個函式一個在爬蟲開始時執行,一個在爬蟲執行結束時執行

檔案程式碼:

'''
下面給出兩種不同型別的內建儲存資料方式,請根據需求使用
 當然也可以不使用內建函式來儲存資料,可以自己寫
'''

# from scrapy.exporters import JsonItemExporter
# 每次會把資料放在記憶體,最後在一次性寫入檔案,儲存的是一個josn型別的檔案
# 壞處是比較耗費記憶體
# class QsbkPipeline:
#     def __init__(self):
#         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):
#         print('爬蟲開始了')
#     def process_item(self, item, spider):
#         self.exporter.export_item(item)
#         return item
#
#     def close_spider(self):
#         self.exporter.finish_exporting()
#         self.fp.close()
#         print('爬蟲結束了')




from itemadapter import ItemAdapter
# 每次呼叫export_item就會把itrm存放在磁碟,壞處是每一次字典是一行,整個檔案不是一個字典,好處是
# 每次寫入磁碟,不會佔用大量記憶體
from scrapy.exporters import JsonLinesItemExporter
class QsbkPipeline:
    def __init__(self):
        self.fp = open('duanzi.json','wb')
        self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
        # self.exporter.start_exporting()

    def open_spider(self,spider):  # 爬蟲開始的時候會執行
        print('爬蟲開始了')

    def process_item(self, item, spider): # 當有item輸出傳來的時候會執行
        # 這樣的話我們就不需要先將item轉換為字典形式再去儲存,我們可以直接傳給匯出器exporter
        self.exporter.export_item(item)
        # 只有下面return了,我們才可以對下一個item進行操作
        return item

    def close_spider(self,spider):  # 爬蟲結束會執行
        # self.exporter.finish_exporting()
        self.fp.close()
        print('爬蟲結束了')

五、qsbk_spider檔案

import scrapy
from scrapy.http.response.html import HtmlResponse
from scrapy.selector.unified import SelectorList
from qsbk.items import QsbkItem

class QsbkSpiderSpider(scrapy.Spider):
    name = 'qsbk_spider'  # 同一類爬蟲名字要相同,以便於命令列下執行
    allowed_domains = ['qiushibaike.com'] # 控制爬蟲範圍,以訪爬蟲通過其他連結,爬到其他網站
    # start_url可以傳一個url也可以多個,這個url會傳給引擎,然後引擎會傳給排程器,排程器又會處理後傳到
    # 引擎,之後引擎傳給下載器,下載器將資料又通過引擎傳遞到爬蟲的parse函式,它的引數response就是返回的資料
    start_urls = ['https://www.qiushibaike.com/text']
    base_url = 'https://www.qiushibaike.com'

    def parse(self, response):
        # selectorlist
        duanzidivs = response.xpath('//*[@id="content"]/div/div[2]/div') #獲取包含所有段子的div
        for duanzidiv in duanzidivs:
            # selector
            # get()方法回去的是selector的第一個文字,get_all()獲取的是所有文字
            author = duanzidiv.xpath(".//div[1]/a[2]/h2/text()").get().strip()
            content = duanzidiv.xpath(".//a[1]/div/span[1]/text()").getall() # getall方法會將所有文字資訊轉變成一個佇列,然後轉換成一個欄位
            content = ".".join(content).strip() # strip方法就是消除content字串前後行
            item = QsbkItem(author=author,content=content)
            yield item  # 讓這個函式變成一個生成器函式
        # 找到下一頁按鈕,並提取出裡面href的內容,之後連結拼接形成新的url
        next_url = response.xpath('//*[@id="content"]/div/div[2]/ul/li[8]/a/@href').get() 
        if not next_url:
            return
        else:
            yield scrapy.Request(self.base_url+next_url,callback=self.parse)

六、xpath的簡單使用

1、選取節點

表示式 描述 例項
nodename 選取nodename節點的所有子節點 xpath(‘//div’) 選取了div節點的所有子節點
/ 從根節點選取 xpath(‘/div’) 從根節點上選取div節點
// 選取所有的當前節點,不考慮他們的位置 xpath(‘//div’) 選取所有的div節點
. 選取當前節點 xpath(‘./div’) 選取當前節點下的div節點
.. 選取當前節點的父節點 xpath(‘..’) 回到上一個節點
@ 選取屬性 xpath(’//@calss’) 選取所有的class屬性

2、嵌在方括號內,用來查詢某個特定的節點或包含某個制定的值的節點

表示式 結果
xpath(‘/body/div[1]’) 選取body下的第一個div節點
xpath(‘/body/div[last()]’) 選取body下最後一個div節點
xpath(‘/body/div[last()-1]’) 選取body下倒數第二個div節點
xpath(‘/body/div[positon()<3]’) 選取body下前兩個div節點
xpath(‘/body/div[@class]’) 選取body下帶有class屬性的div節點
xpath(‘/body/div[@class=”main”]’) 選取body下class屬性為main的div節點
xpath(‘/body/div[price>35.00]’) 選取body下price元素值大於35的div節點

3、萬用字元

表示式 結果
xpath(’/div/*’) 選取div下的所有子節點
xpath(‘/div[@*]’) 選取所有帶屬性的div節點

4、相對路徑查詢

軸名稱 表示式 描述
ancestor xpath(‘./ancestor::*’) 選取當前節點的所有先輩節點(父、祖父)
ancestor-or-self xpath(‘./ancestor-or-self::*’) 選取當前節點的所有先輩節點以及節點本身
attribute xpath(‘./attribute::*’) 選取當前節點的所有屬性
child xpath(‘./child::*’) 返回當前節點的所有子節點
descendant xpath(‘./descendant::*’) 返回當前節點的所有後代節點(子節點、孫節點)
following xpath(‘./following::*’) 選取文件中當前節點結束標籤後的所有節點
following-sibing xpath(‘./following-sibing::*’) 選取當前節點之後的兄弟節點
parent xpath(‘./parent::*’) 選取當前節點的父節點
preceding xpath(‘./preceding::*’) 選取文件中當前節點開始標籤前的所有節點

preceding-sibling xpath(‘./preceding-sibling::*’) 選取當前節點之前的兄弟節點
self xpath(‘./self::*’) 選取當前節點

5、模糊查詢

函式 用法 解釋
starts-with xpath(‘//div[starts-with(@id,”ma”)]‘) 選取id值以ma開頭的div節點
contains xpath(‘//div[contains(@id,”ma”)]‘) 選取id值包含ma的div節點
and xpath(‘//div[contains(@id,”ma”) and contains(@id,”in”)]‘) 選取id值包含ma和in的div節點
text() xpath(‘//div[contains(text(),”ma”)]‘) 選取節點文字包含ma的div節點