python Scrapy爬蟲案例 (一)

需求

爬取古詩文網(wǎng)站中的 詩詞的 標題、作者、朝代、內(nèi)容、以及翻頁并保存


image.png

工具和環(huán)境

語言:python 3.6
IDE: Pycharm
瀏覽器:Chrome
爬蟲框架:Scrapy 2.5.0

爬蟲實現(xiàn)

第一步:頁面分析

第一頁:https://www.gushiwen.org/default_1.aspx
第二頁:https://www.gushiwen.cn/default_2.aspx 點擊"下一頁"按鈕
第三頁:https://www.gushiwen.cn/default.aspx?page=3 在數(shù)字框里面輸入3(無視了)
第三頁:https://www.gushiwen.cn/default_3.aspx 點擊"下一頁"按鈕

image.png

在第二頁的基礎(chǔ)上點擊“下一頁”按鈕得到的URL地址是:https://www.gushiwen.cn/default_3.aspx
這個和上面分析的第一頁和第二頁的URL地址相似,所以我們忽視在“數(shù)字框里面輸入數(shù)字”得到的URL地址。

觀察頁面URL發(fā)現(xiàn)有兩個域名:“gushiwen.org”,“gushiwen.cn”
這兩個域名都可以使用, .org 和 .cn 是一樣的效果。

第二步:創(chuàng)建scrapy項目

在Pycharm終端里面輸入如下命令創(chuàng)建scrapy項目
1.到項目所在的目錄下創(chuàng)建scrapy項目:scrapy startproject gsw
2.進入到gsw目錄下創(chuàng)建爬蟲:scrapy genspider gs gushiwen.org

第三步:代碼邏輯實現(xiàn)

代碼實現(xiàn)主要分如下的幾步:
1.分析第一個URL頁面HTML標簽結(jié)構(gòu)并提取數(shù)據(jù)
2.用item封裝提取到的數(shù)據(jù)并傳遞給管道
3.管道文件里實現(xiàn)數(shù)據(jù)的保存
4.實現(xiàn)其他頁面數(shù)據(jù)的爬取---翻頁

分析第一個URL頁面HTML標簽結(jié)構(gòu)并提取數(shù)據(jù)

選中某一首詩的標題或詩文,點擊鼠標右鍵菜單中的“檢查”按鈕


image.png

可以查看到網(wǎng)頁對應的前端HTML標簽,并自動給我們定位到了這個標題HTML標簽的位置,如標題<b>螽斯</b>


image.png

通過該標題標簽一步步推導,發(fā)現(xiàn)”每首詩”的布局都是一個<div class=”sons”>…</div>的標簽內(nèi),然后頁面中的所有的“詩”都在共同的標簽<div class=”left”>…</div>標簽下
image.png

初步分析后,我們嘗試獲取“詩的標題”,修改代碼:

import scrapy
from gsw.items import GswItem


class GsSpider(scrapy.Spider):
    name = 'gs'
    allowed_domains = ['gushiwen.org', 'gushiwen.cn']  # 修改,增加一個相似域名
    start_urls = ['https://www.gushiwen.cn/default_1.aspx']  # 修改為起始第一個URL地址,爬蟲程序就是從這一頁開始數(shù)據(jù)的

    # 該爬蟲先從上面起始的第一個URL地址開始發(fā)出請求,并得到請求響應的數(shù)據(jù),得到響應數(shù)據(jù)后,數(shù)據(jù)的解析就用如下的parse函數(shù)進行解析
    def parse(self, response):
        gsw_divs = response.xpath('//div[@class="left"]/div[@class="sons"]')  # xpath返回的是列表(每個div sons標簽,也就是每首詩)
        for gsw_div in gsw_divs:  # 對每首詩進行遍歷
            title = gsw_div.xpath('.//b/text()').extract_first()  # xpath返回的是列表(取列表第一個也就是詩的標題)
            print(title)

打印輸出結(jié)果: 結(jié)果中含有None說明對應的div中沒有<b>標簽,說明頁面中該部分顯示的不是一首詩


image.png

接著獲取 詩的 作者 和 朝代


image.png
import scrapy
from gsw.items import GswItem


class GsSpider(scrapy.Spider):
    name = 'gs'
    allowed_domains = ['gushiwen.org', 'gushiwen.cn']  # 修改,增加一個相似域名
    start_urls = ['https://www.gushiwen.cn/default_1.aspx']  # 修改為起始第一個URL地址,爬蟲程序就是從這一頁開始數(shù)據(jù)的

    # 該爬蟲先從上面起始的第一個URL地址開始發(fā)出請求,并得到請求響應的數(shù)據(jù),得到響應數(shù)據(jù)后,數(shù)據(jù)的解析就用如下的parse函數(shù)進行解析
    def parse(self, response):
        gsw_divs = response.xpath('//div[@class="left"]/div[@class="sons"]')  # xpath返回的是列表(每個div sons標簽,也就是每首詩)
        for gsw_div in gsw_divs:  # 對每首詩進行遍歷
            title = gsw_div.xpath('.//b/text()').extract_first()  # xpath返回的是列表(取列表第一個也就是詩的標題)
            if title:
                source = gsw_div.xpath('.//p[@class="source"]/a/text()').extract()  # 獲取 作者 和 朝代 的列表
                author = source[0]   # 獲取 作者
                dynasty = source[1]  # 獲取 朝代

接著獲取 詩的內(nèi)容


image.png
import scrapy
from gsw.items import GswItem


class GsSpider(scrapy.Spider):
    name = 'gs'
    allowed_domains = ['gushiwen.org', 'gushiwen.cn']  # 修改,增加一個相似域名
    start_urls = ['https://www.gushiwen.cn/default_1.aspx']  # 修改為起始第一個URL地址,爬蟲程序就是從這一頁開始數(shù)據(jù)的

    # 該爬蟲先從上面起始的第一個URL地址開始發(fā)出請求,并得到請求響應的數(shù)據(jù),得到響應數(shù)據(jù)后,數(shù)據(jù)的解析就用如下的parse函數(shù)進行解析
    def parse(self, response):
        gsw_divs = response.xpath('//div[@class="left"]/div[@class="sons"]')  # xpath返回的是列表(每個div sons標簽,也就是每首詩)
        for gsw_div in gsw_divs:  # 對每首詩進行遍歷
            title = gsw_div.xpath('.//b/text()').extract_first()  # xpath返回的是列表(取列表第一個也就是詩的標題)
            if title:
                source = gsw_div.xpath('.//p[@class="source"]/a/text()').extract()  # 獲取 作者 和 朝代 的列表
                author = source[0]   # 獲取 作者
                dynasty = source[1]  # 獲取 朝代
                content_lst = gsw_div.xpath('.//div[@class="contson"]//text()').extract()  # 獲取 詩的文本內(nèi)容
                content = ''.join(content_lst).strip()  # 列表中的每個元素用空串拼接,去除列表并用strip()方法移除字符串頭尾指定的字符

至此 詩的 標題、作者、朝代、內(nèi)容 這些數(shù)據(jù)都拿到了。。。
接下來實現(xiàn)翻頁并保存數(shù)據(jù)

用item封裝提取到的數(shù)據(jù)并傳遞給管道

來看下scrapy項目的目錄結(jié)構(gòu)


image.png

gs.py:是我們實現(xiàn)的爬蟲文件
items.py:是實現(xiàn)數(shù)據(jù)的封裝,提前定義好要爬取的數(shù)據(jù)內(nèi)容
pipelines.py:是實現(xiàn)數(shù)據(jù)的保存
settings.py:是scrapy項目相關(guān)的設(shè)置
middlewares.py:以后用到再講

我們在items.py里定義好我們要爬取的內(nèi)容

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class GswItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    author = scrapy.Field()
    dynasty = scrapy.Field()
    content = scrapy.Field()
    pass

然后在爬蟲文件里面導入這個items.py里面定義的數(shù)據(jù)類GswItem
導入方式有兩種:
1.從項目最開始目錄開始一級一級導入
from projectRootDir.demo.gsw.gsw.items import GswItem
2.將創(chuàng)建scrapy項目的目錄作為根目錄 Mark Directory as -> Source Root
from gsw.items import GswItem
然后在爬蟲文件里面創(chuàng)建items對象并把數(shù)據(jù)賦值給items對象,賦值的方式見源碼,最后把數(shù)據(jù)傳遞給管道文件pipelines.py

import scrapy
from gsw.items import GswItem


class GsSpider(scrapy.Spider):
    name = 'gs'
    allowed_domains = ['gushiwen.org', 'gushiwen.cn']  # 修改,增加一個相似域名
    start_urls = ['https://www.gushiwen.cn/default_1.aspx']  # 修改為起始第一個URL地址,爬蟲程序就是從這一頁開始數(shù)據(jù)的

    # 該爬蟲先從上面起始的第一個URL地址開始發(fā)出請求,并得到請求響應的數(shù)據(jù),得到響應數(shù)據(jù)后,數(shù)據(jù)的解析就用如下的parse函數(shù)進行解析
    def parse(self, response):
        gsw_divs = response.xpath('//div[@class="left"]/div[@class="sons"]')  # xpath返回的是列表(每個div sons標簽,也就是每首詩)
        for gsw_div in gsw_divs:  # 對每首詩進行遍歷
            title = gsw_div.xpath('.//b/text()').extract_first()  # xpath返回的是列表(取列表第一個也就是詩的標題)
            if title:
                source = gsw_div.xpath('.//p[@class="source"]/a/text()').extract()  # 獲取 作者 和 朝代 的列表
                author = source[0]   # 獲取 作者
                dynasty = source[1]  # 獲取 朝代
                content_lst = gsw_div.xpath('.//div[@class="contson"]//text()').extract()  # 獲取 詩的文本內(nèi)容
                content = ''.join(content_lst).strip()  # 列表中的每個元素用空串拼接,去除列表并用strip()方法移除字符串頭尾指定的字符
                # 方式一
                # item = GswItem()  # 創(chuàng)建items對象并賦值
                # item['title'] = title
                # item['author'] = author
                # item['dynasty'] = dynasty
                # item['content'] = content
                # 方式二
                item = GswItem(title=title, author=author, dynasty=dynasty, content=content)  # 創(chuàng)建items對象并賦值
                yield item  # 將item數(shù)據(jù)傳給管道 pipelines

注意:item與管道之間數(shù)據(jù)傳遞時,需要在settings.py里面把item pipelines相關(guān)的設(shè)置項打開

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'gsw.pipelines.GswPipeline': 300,
}
管道文件里實現(xiàn)數(shù)據(jù)的保存

接下來我們到pipelines.py文件中處理數(shù)據(jù)的保存,簡單數(shù)據(jù)保存如下

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
import json


class GswPipeline:

    def open_spider(self, spider):
        self.fp = open('gsw.txt', 'w', encoding='utf-8')

    # item 是從爬蟲文件傳遞過來的數(shù)據(jù),是GswItem類的一個實例化對象
    def process_item(self, item, spider):
        item_json = json.dumps(dict(item), ensure_ascii=False)
        self.fp.write(item_json + '\n')
        return item

    def close_spider(self, spider):
        self.fp.close()
實現(xiàn)其他頁面數(shù)據(jù)的爬取---翻頁

實現(xiàn)自動翻頁一般有兩種方法:
1.在頁面中找到下一頁的地址;
2.自己根據(jù)URL的變化規(guī)律構(gòu)造所有頁面地址;
一般情況下我們使用第一種方法,第二種方法適用于頁面的下一頁地址為JS加載的情況。我們現(xiàn)在只說第一種方法。
首先到首頁的最底部找到下一頁的按鈕,然后點擊右鍵檢查,會自動定位到“下一頁”按鈕元素位置處。

image.png

通過第一頁請求響應到的response數(shù)據(jù)解析到下一頁的鏈接:
next_href = response.xpath('//a[@id="amore"]/@href').get().get())
然后重新調(diào)用scrapy.Request(next_url)函數(shù)發(fā)出下一頁數(shù)據(jù)的請求,重新走一遍和第一頁數(shù)據(jù)請求一樣的完整流程(爬蟲請求 爬蟲引擎 調(diào)度器 爬蟲引擎 下載器 爬蟲引擎 爬蟲響應 管道 …)

注意:scrapy.Request第二個參數(shù)callback是響應數(shù)據(jù)解析處理函數(shù),下一頁的數(shù)據(jù)解析函數(shù)和第一頁是相同的(parse)所以可以省略不寫,如果不同就需要另外編寫比如每首詩的詳情頁,后面補充。。。

翻頁的邏輯代碼實現(xiàn)如下:

        # 翻頁邏輯處理
        next_href = response.xpath('//a[@id="amore"]/@href').get()
        if next_href:
            # next_url = 'https://www.gushiwen.cn' + next_href[0]  # URL第一種補全方式
            next_url = response.urljoin(next_href)  # URL第二種補全方式(urljoin可以進行URL地址的補全)
            yield scrapy.Request(next_url)  # 爬蟲程序重新通過 爬蟲引擎 調(diào)度器 下載器 請求下一頁的數(shù)據(jù)
            # yield scrapy.Request(url=next_url, callback=self.parse)  # 或者這種方式請求

完整的爬蟲程序如下:

import scrapy
from gsw.items import GswItem


class GsSpider(scrapy.Spider):
    name = 'gs'
    allowed_domains = ['gushiwen.org', 'gushiwen.cn']  # 修改,增加一個相似域名
    start_urls = ['https://www.gushiwen.cn/default_1.aspx']  # 修改為起始第一個URL地址,爬蟲程序就是從這一頁開始數(shù)據(jù)的

    # 該爬蟲先從上面起始的第一個URL地址開始發(fā)出請求,并得到請求響應的數(shù)據(jù),得到響應數(shù)據(jù)后,數(shù)據(jù)的解析就用如下的parse函數(shù)進行解析
    def parse(self, response):
        gsw_divs = response.xpath('//div[@class="left"]/div[@class="sons"]')  # xpath返回的是列表(每個div sons標簽,也就是每首詩)
        for gsw_div in gsw_divs:  # 對每首詩進行遍歷
            title = gsw_div.xpath('.//b/text()').extract_first()  # xpath返回的是列表(取列表第一個也就是詩的標題)
            if title:
                source = gsw_div.xpath('.//p[@class="source"]/a/text()').extract()  # 獲取 作者 和 朝代 的列表
                author = source[0]   # 獲取 作者
                dynasty = source[1]  # 獲取 朝代
                content_lst = gsw_div.xpath('.//div[@class="contson"]//text()').extract()  # 獲取 詩的文本內(nèi)容
                content = ''.join(content_lst).strip()  # 列表中的每個元素用空串拼接,去除列表并用strip()方法移除字符串頭尾指定的字符
                # 方式一
                # item = GswItem()  # 創(chuàng)建items對象并賦值
                # item['title'] = title
                # item['author'] = author
                # item['dynasty'] = dynasty
                # item['content'] = content
                # 方式二
                item = GswItem(title=title, author=author, dynasty=dynasty, content=content)  # 創(chuàng)建items對象并賦值
                yield item  # 將item數(shù)據(jù)傳給管道 pipelines

        # 翻頁邏輯處理
        next_href = response.xpath('//a[@id="amore"]/@href').get()
        if next_href:
            # next_url = 'https://www.gushiwen.cn' + next_href[0]  # URL第一種補全方式
            next_url = response.urljoin(next_href)  # URL第二種補全方式(urljoin可以進行URL地址的補全)
            yield scrapy.Request(next_url)  # 爬蟲程序重新通過 爬蟲引擎 調(diào)度器 下載器 請求下一頁的數(shù)據(jù)
            # yield scrapy.Request(url=next_url, callback=self.parse)  # 或者這種方式請求
程序運行結(jié)果:

打開在管道文件中創(chuàng)建的“gsw.txt”發(fā)現(xiàn)有50首詩,總共有5頁,每頁10首,數(shù)據(jù)爬取正確。


image.png

結(jié)尾和補充

1.當爬取的范圍不一樣的時候(域名不一樣的時候)
allowed_domains = ['gushiwen.org', 'gushiwen.cn'] 可以加多個
2.注意處理列表為空的數(shù)據(jù)(通過非空判斷,通過try語句等)
3.翻頁處理(1.可以找頁數(shù)的規(guī)律;2.直接找下一頁的URL,然后yield scrappy.Request(next_url))
4.遇到URL不全的時候,建議補全URL地址(1.拼串;2.urljoin() 這個是scrapy提供的)
5.在settings.py里面除了打開item pipelines配置項外,也需要修改默認請求頭,增加User-Agent請求頭

# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
  'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36',
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
}

配置打印級別:LOG_LEVEL = 'WARNING'
Obey rules改成False:ROBOTSTXT_OBEY = False

BOT_NAME = 'gsw'

SPIDER_MODULES = ['gsw.spiders']
NEWSPIDER_MODULE = 'gsw.spiders'
LOG_LEVEL = 'WARNING'


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'gsw (+http://www.yourdomain.com)'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容