Scrapy爬取全站數據並存儲到數據庫和文件中


  • scrapy五大核心組件簡介

    • 引擎(Scrapy)

      • 用來處理整個系統的數據流處理, 觸發事務(框架核心)

    • 調度器(Scheduler)

      • 用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 可以想像成一個URL(抓取網頁的網址或者說是鏈接)的優先隊列, 由它來決定下一個要抓取的網址是什么, 同時去除重復的網址
    • 下載器(Downloader)

      • 用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效的異步模型上的)
    • 爬蟲(Spiders)

      • 爬蟲是主要干活的, 用於從特定的網頁中提取自己需要的信息, 即所謂的實體(Item)。用戶也可以從中提取出鏈接,讓Scrapy繼續抓取下一個頁面
    • 項目管道(Pipeline)

      • 負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證實體的有效性、清除不需要的信息。當頁面被爬蟲解析后,將被發送到項目管道,並經過幾個特定的次序處理數據

爬取全站數據

大部分的網站展示的數據都進行了分頁操作,那么將所有頁碼對應的頁面數據進行爬取就是爬蟲中的全站數據爬取。

基於scrapy如何進行全站數據爬取呢?

使用Request方法手動發起請求。

需求:將糗事百科所有頁碼的作者和段子內容數據進行爬取切持久化存儲

spider文件:

class QiubaiSpider(scrapy.Spider):
    name = 'qiubai'  # 應用名稱(唯一標識)
    # 允許爬取的域名(如果遇到非該域名的url則爬取不到數據)
    allowed_domains = ['https://www.qiushibaike.com/']
    # 起始爬取的url
    start_urls = ['https://www.qiushibaike.com/text']

    # 爬取多頁
    pageNum = 1  # 起始頁碼
    # 統用的url模板(不可變)
    url = 'https://www.qiushibaike.com/text/page/%s/'  # 每頁的url

    # 訪問起始URL並獲取結果后的回調函數,該函數的response參數就是向起始的url發送請求后,獲取的響應對象.該函數返回值必須為可迭代對象或者NUll
    def parse(self, response):
        # print(response.text)  # 獲取字符串類型的響應內容
        # 獲取作者名稱和內容
        # print(response.body)  # 獲取字節類型的相應內容
        # xpath為response中的方法,可以將xpath表達式直接作用於該函數中
        odiv = response.xpath('//div[@class="col1 old-style-col1"]/div')
        # print(len(odiv))
        content_list = []  # 用於存儲解析到的數據
        for div_item in odiv:
            # xpath函數返回的為列表,列表中存放的數據為Selector類型的數據。
            # 我們解析到的內容被封裝在了Selector對象中,需要調用extract()函數將解析的內容從Selecor中取出。
            author = div_item.xpath('.//div[1]/a[2]/h2/text() | .//div[1]/span/h2/text()')[0].extract()
            content = div_item.xpath('.//div[@class="content"]/span/text()').extract()
            content = ''.join(content)  # 列表轉換為字符串
            # 打印展示爬取到的數據
            # print(author, content)
            # print(content)
            author = author.strip('\n')  # 過濾空行
            content = content.strip('\n')
            # 將解析到的數據封裝至items對象中
            item = FirstbloodItem()
            item['author'] = author
            item['content'] = content
            print(author)

            yield item  # 提交item到管道文件(pipelines.py)

        # 爬取所有頁碼數據
        print('pageNum={}'.format(self.pageNum))
        if self.pageNum < 5:  # 一共爬取13頁(共13頁)
            self.pageNum += 1
            new_url = format(self.url % self.pageNum)
            print(new_url)
            # 遞歸爬取數據:callback參數的值為回調函數(將url請求后,得到的相應數據繼續進行parse解析),遞歸調用parse函數
            # 在 scrapy.Request() 函數中將參數 dont_filter=True 設置為 True,使 requests 不被過濾:
            yield scrapy.http.Request(url=new_url, callback=self.parse, dont_filter=True)

items.py文件

import scrapy


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

pipeline文件

import pymysql
class FirstbloodPipeline(object):
    # 構造方法
    def __init__(self):
        self.fp = None  # 定義一個文件描述符屬性

    # 下列都是在重寫父類的方法:
    # 開始爬蟲時,執行一次
    def open_spider(self, spider):
        print('爬蟲開始')
        self.fp = open('./data.txt', 'w',encoding='utf-8')
    # 專門處理item對象
    # 因為該方法會被執行調用多次,所以文件的開啟和關閉操作寫在了另外兩個只會各自執行一次的方法中。
    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        contents = '{}:{}\n\n'.format(author,content)
        self.fp.write(contents)
        return item  # 傳遞到下一個被執行的管道類

    def close_spider(self, spider):
        print('爬蟲結束')
        self.fp.close()

#管道文件中的一個管道類對應的是講數據存儲到一種平台
#爬蟲文件提交的item只會給管道文件中第一個被執行的管道類接受
#process_item的return item表示item傳遞到下一個被執行的管道類
#將數據存儲到數據庫
class mysqlPipeline(object):
    # 構造方法
    def __init__(self):
        self.conn = None  # 定義一個文件描述符屬性
        self.cursor = None
        self.num = 0

    # 下列都是在重寫父類的方法:
    # 開始爬蟲時,執行一次
    def open_spider(self, spider):
        self.conn = pymysql.Connect(host='192.168.31.xx',port=3306,user='root',password='111',db='xxx_db',charset='utf8')
        print('爬蟲數據庫開始')

    # 專門處理item對象
    # 因為該方法會被執行調用多次,所以文件的開啟和關閉操作寫在了另外兩個只會各自執行一次的方法中。
    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        self.cursor = self.conn.cursor()
        try:

            self.cursor.execute('insert into qiubai values(%s,%s)' ,(author,content))
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        print('爬蟲數據庫結束')
        self.cursor.close()
        self.conn.close()

setting

USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'  # 偽裝請求載體身份
# Obey robots.txt rules
# ROBOTSTXT_OBEY = True
ROBOTSTXT_OBEY = False  #可以忽略或者不遵守robots協議
#只顯示指定類型的日志信息
LOG_LEVEL='ERROR'
# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32



# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'firstBlood.pipelines.FirstbloodPipeline': 300,
    'firstBlood.pipelines.mysqlPipeline': 320,

    #300表示優先級,越小越高
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM