1. 程式人生 > >scrapy爬蟲框架(四):scrapy中 yield使用詳解

scrapy爬蟲框架(四):scrapy中 yield使用詳解

開始前的準備工作:

MySQL下載:點我
python MySQL驅動下載:pymysql(pyMySql,直接用pip方式安裝)

全部安裝好之後,我們來熟悉一下pymysql模組

import pymysql

#建立連結物件
connection = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='1234', db='python')
#建立遊標 遊標用來進行查詢,修改等操作
cursor = connection.cursor()

#定義sql語句 這裡的sql語法根據使用的資料庫不同會有一些小差別
sql = "SELECT * FROM python.text_info where text_title='test'"

#執行sql語句 返回受到影響的行數
cursor.execute(sql)

#獲取sql語句執行後的返回資料 預設返回的資料型別為元組
#獲取所有返回
r = cursor.fetchall()
#獲取一個返回
r = cursor.fetchone()
#獲取至多三個返回 不足三個時返回所有
r = cursor.fetchmany(3)
#其他的fetch方法可自行百度

#將返回資料型別改為字典
cursor = connection.cursor(cursor=pymysql.cursors.DictCursor)
#或者在建立連線物件時指定返回資料型別為字典 建議把返回型別修改為字典型別
connection = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='1234', db='python', cursorclass=pymysql.cursors.DictCursor)

#儲存所做的修改 在連線關閉之前,如果你沒有呼叫下面的語句
#那麼,你之前的所有修改將不會被儲存
connection.commit()

#關閉遊標
cursor.close()
#關閉連線
connection.close()

一、確定items

我們要爬取的網站是:http://m.50zw.la
要爬取的是小說的資訊,如圖:

8516750-a43030af8cee4412.png

所以items.py檔案如下:

import scrapy


class TextInfoItem(scrapy.Item):
    # name = scrapy.Field()
    text_name = scrapy.Field()
    text_author = scrapy.Field()
    text_type = scrapy.Field()
    text_status = scrapy.Field()
    text_latest = scrapy.Field()
    text_intro = scrapy.Field()

最後資訊是要儲存到資料庫裡的,所以我們還得建立一個數據庫表。

  • 第一步:在開始選單裡找到MySQL Workbench,雙擊開啟。MySQL Workbench是MySQL自帶的一個視覺化管理工具
  • 第二步:在 MySQL Workbench裡連線資料庫,並建立一個數據庫 python,然後再在剛剛建立的資料庫裡建立一個表 text_info
  • 第三步:在 text_info表裡逐一新增 text_name,text_author 等屬性,型別全部設定為 varchar,大小除了 text_intro是 1000外,其他的全部設定為 50

MySQL的使用就不詳細講了。如果遇到問題,歡迎評論留言。

二、爬取資訊

為了簡單,我們只爬取 50zw網站下的玄幻分類的小說資訊。

細節前面已經講過了,這裡不再多講,有不懂的可以去看前面的幾篇文章。

廢話不多說,直接上程式碼:

import scrapy
from text_info.items import TextInfoItem

class A50zwSpider(scrapy.Spider):
    name = '50zw'
    allowed_domains = ['m.50zw.la']
    start_urls = ['http://m.50zw.la/wapsort/1_1.html']

    #主站連結 用來拼接
    base_site = 'http://m.50zw.la'

    def parse(self, response):
        book_urls = response.xpath('//table[@class="list-item"]//a/@href').extract()

        for book_url in book_urls:
            url = self.base_site + book_url
            yield scrapy.Request(url, callback=self.getInfo)

        #獲取下一頁
        next_page_url = self.base_site + response.xpath('//table[@class="page-book"]//a[contains(text(),"下一頁")]/@href').extract()[0]
        yield scrapy.Request(next_page_url, callback=self.parse)

    def getInfo(self, response):
        item = TextInfoItem()

        #提取資訊
        item['text_id'] = response.url.split('_')[1].replace('/', '')
        item['text_name'] = response.xpath('//table[1]//p/strong/text()').extract()[0]
        item['text_author'] = response.xpath('//table[1]//p/a/text()').extract()[0]
        item['text_type'] = response.xpath('//table[1]//p/a/text()').extract()[1]
        item['text_status'] = response.xpath('//table[1]//p/text()').extract()[2][3:]
        item['text_latest'] = response.xpath('//table[1]//p[5]/text()').extract()[0][3:]
        item['text_intro'] = response.xpath('//div[@class="intro"]/text()').extract()[0]

        yield item

這裡我們通過 yield 來發起一個請求,並通過 callback 引數為這個請求添加回調函式,在請求完成之後會將響應作為引數傳遞給回撥函式。

scrapy框架會根據 yield 返回的例項型別來執行不同的操作,如果是 scrapy.Request 物件,scrapy框架會去獲得該物件指向的連結並在請求完成後呼叫該物件的回撥函式。

如果是 scrapy.Item 物件,scrapy框架會將這個物件傳遞給 pipelines.py做進一步處理。

這裡我們有三個地方使用了 yield ,第一個地方是:

 for book_url in book_urls:
        url = self.base_site + book_url
        yield scrapy.Request(url, callback=self.getInfo)

這裡我們在迴圈裡不斷提取小說詳細頁面的連結,並通過 yield 來發起請求,並且還將函式 getInfo 作為回撥函式來從響應中提取所需的資料。

第二個地方是:

#獲取下一頁
next_page_url = self.base_site + response.xpath('//table[@class="page-book"]//a[contains(text(),"下一頁")]/@href').extract()[0]
yield scrapy.Request(next_page_url, callback=self.parse)

這裡是在爬取完一頁的資訊後,我們在當前頁面獲取到了下一頁的連結,然後通過 yield 發起請求,並且將 parse 自己作為回撥函式來處理下一頁的響應。

這有點像遞迴,不過遞迴是函式自己呼叫自己,這裡看起來好像是 parse 呼叫了自己,但實際上 parse 是由 scrapy框架在獲得響應後呼叫的。

最後一處使用了 yield 的地方在 getInfo 函式裡:

def getInfo(self, response):
        item = TextInfoItem()
        
        ... ...
        
        item['text_intro'] = response.xpath('//div[@class="intro"]/text()').extract()[0]
        yield item

這裡我們通過 yield 返回的不是 Request 物件,而是一個 TextInfoItem 物件。

scrap有框架獲得這個物件之後,會將這個物件傳遞給 pipelines.py來做進一步處理。

我們將在 pipelines.py裡將傳遞過來的 scrapy.Item 物件儲存到資料庫裡去。

三、將資訊插入資料庫

python對資料庫的操作很簡單,我們簡單瞭解一下步驟:

  1. 建立資料庫連線
  2. 建立操作遊標
  3. 寫sql語句
  4. 執行sql語句
  5. 如果執行的是查詢語句,則用fetch語句獲取查詢結果
  6. 如果執行的是插入、刪除等對資料庫造成了影響的sql語句,還需要執行commit儲存修改

貼上程式碼:

import pymysql

class TextInfoPipeline(object):
    def __init__(self):
        #建立資料庫連線
        self.connection = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='1234', db='python',charset='utf8')
        #建立操作遊標
        self.cursor = self.connection.cursor()

    def process_item(self, item, spider):
        #定義sql語句
        sql = "INSERT INTO `python`.`text_info` (`text_id`, `text_name`, `text_author`, `text_type`, `text_status`, `text_latest`, `text_intro`) VALUES ('"+item['text_id']+"', '"+item['text_name']+"', '"+item['text_author']+"', '"+item['text_type']+"', '"+item['text_status']+"', '"+item['text_latest']+"', '"+item['text_intro']+"');"
        
        #執行sql語句
        self.cursor.execute(sql)
        #儲存修改
        self.connection.commit()

        return item

    def __del__(self):
        #關閉操作遊標
        self.cursor.close()
        #關閉資料庫連線
        self.connection.close()

寫在最後:

  1. 程式碼敲好後不要忘記在settings裡開啟pipelines

  2. pymsql連線時預設的編碼是latin-1,所以在建立資料庫連線時會增加引數charset來修改編碼,要修改為utf-8的話得用charset=’utf8‘,而不是charset=’utf-8‘

  3. 這個網站有些問題,會時不時報404錯誤,所以在爬的過程中會報list index out of range,這是因為得到了錯誤的網頁,xpath找不到對應得路徑返回了空列表。這是正常現象,並不是程式碼出問題了(當然,如果頻繁報錯最好是檢查一下程式碼)

貼一張成功後的圖片:


8516750-917f02f1d44f01cd.png

最後的最後,覺得我寫的不錯的話記得關注我哦。