Python 爬蟲 Aiohttp 爬各大小說網站排行榜

簡介

用於Asyncio(Python3.4開始引用的標準函數庫)和Python的非同步HTTP客戶端/服務器。

可開發服務端和使用者端,也可開發網路爬蟲。

由於Asyncio具有高平行處理的特性,因此Aiohttp繼承了Asyncio的特性,使得Aiohttp特別適合開發網路爬蟲。

安裝

$ pip install aiohttp

為了通過客戶端API加快DNS解析速度,您也可以安裝 aiodns。強烈建議使用此選項:

$ pip install aiodns

完全安裝包

下面將讓你直接安裝aiohttp附帶chardet、 aiodns。無需再單個單個安裝。

$ pip install aiohttp[speedups]

Aiohttp模組安裝完成後,在CMD視窗進入Python的互動模式,匯入Aiohttp模組並驗證模組安裝是否成功:

E:\>python
>>> import aiohttp
>>> aiohttp.__version__
'3.6.2'

使用Aiohttp的使用者端功能

首先要建立一個階段物件session,然後利用session去存取網頁,基本用法如下:

import aiohttp
import asyncio
async def hello(URL):
  async with aiohttp.ClientSession() as session:
    async with session.get(URL) as response:
      response = await response.text()
      print(response)
if __name__ == '__main__':
  URL = 'http://python.org'
  loop = asyncio.get_event_loop()
  loop.run_until_complete(hello(URL))

上方程式碼是使用Aiohttp與Asyncio模組造訪Python官網。

函數hello()加入了Python內建的關鍵字async和await,這是將函數設定為非同步作業,這是Aiohttp的使用方式;而函數hello()的呼叫和執行需要藉助Asyncio模組,Aiohttp只實現網站的存取方式,而程式的執行過程則由Asyncio模組實現。

Aiohttp定義了多種HTTP請求方法,最常用的是GET和POST

GET

GET請求有兩種形式,分別為沒有參數有參數,使用方法如下:

# 沒有參數
URL = 'http://httpbin.org/get'
async with ClientSession() as session:
  async with session.get(URL) as response:
    response = await response.text()
    print(response)
# 有參數
URL = 'http://httpbin.org/get?key=python'
async with ClientSession() as session:
  async with session.get(URL) as response:
    response = await response.text()
    print(response)
# 設定請求參數params
URL = 'http://httpbin.org/get'
params = {'key':'python'}
async with ClientSession() as session:
  async with session.get(URL,params=params) as response:
    response = await response.text()
    print(response)

POST

一般情況下,發送POST請求都會帶有請求參數,參數值會被包含在請求本體中,然後平行處理到網站伺服器,參數值的資料格式可以為字典、JSON、字串和位元組流,不同的資料格式實現不同的功能,使用方式如下:

# 以字典格式寫入
URL = 'http://httpbin.org/post'
data = {'key':'python'}
async with ClientSession() as session:
  async with session.post(URL,data = data) as response:
    response = await response.text()
    print(response)
# 以JSON格式寫入
URL = 'http://httpbin.org/post'
data = {'key':'python'}
async with ClientSession() as session:
  async with session.post(URL,json = data) as response:
    response = await response.text()
    print(response)
# 以字串格式寫入
URL = 'http://httpbin.org/post'
data = 'python'
async with ClientSession() as session:
  async with session.post(URL,data = data) as response:
    response = await response.text()
    print(response)
# 以位元組流格式寫入
URL = 'http://httpbin.org/post'
data = {'file':open('log.txt','rb')}
async with ClientSession() as session:
  async with session.post(URL,data = data) as response:
    response = await response.text()
    print(response)

更詳細的話可以看官方文檔,這邊就不多提了。

爬取起點小說排行榜

實現過程

1.爬取物件是起點小說網的24小時熱銷榜。

2.資料清洗會使用BeautifulSoup4實現。

3.資料以CSV檔案進行儲存。

開始前先使用下方指令安裝BeautifulSoup4

pip install beautifulsoup4

爬取排行榜

開啟起點小說網24小時熱銷榜,網址為:https://www.qidian.com/rank/hotsales?style=1

將開發人員工具打開,選擇「Network」-「Docs」-刷新頁面,選擇跟網址同名的檔案,切換到「Response」,找到排行第一本小說那段程式碼:

20200505154343 Python 爬蟲 Aiohttp 爬各大小說網站排行榜

可以發現每本小說都有一個class為book-mid-info的區塊,該區塊包含了小說名稱、作者、分類、簡介…等小說的資訊,而我們需要爬的就是這區塊內的內容。

<div class="book-mid-info">
  <h4><a href="//book.qidian.com/info/1010868264" target="_blank" data-eid="qd_C40" data-bid="1010868264">诡秘之主</a></h4>
  <p class="author">
  <img src="//qidian.gtimg.com/qd/images/ico/user.f22d3.png"><a class="name" href="//me.qidian.com/authorIndex.aspx?id=4362088" target="_blank" data-eid="qd_C41">爱潜水的乌贼</a><em>|</em><a href="//www.qidian.com/xuanhuan" target="_blank" data-eid="qd_C42">玄幻</a><em>|</em><span>连载</span>
  </p>
  <p class="intro">
  蒸汽与机械的浪潮中,谁能触及非凡?历史和黑暗的迷雾里,又是谁在耳语?我从诡秘中醒来,睁眼看见这个世界:枪械,大炮,巨舰,飞空艇,差分机;魔药,占卜,诅咒,倒吊人,封印物……光明依旧
  </p>
  <p class="update"><a href="//vipreader.qidian.com/chapter/1010868264/534989929" target="_blank" data-eid="qd_C43" data-bid="1010868264" data-cid="//vipreader.qidian.com/chapter/1010868264/534989929">最新更新 完本感言(下)</a><em>&#183;</em><span>2020-05-02 12:30</span>
  </p>
  </div>

並且由此段程式碼中,可以得出Doc標籤的請求位址與瀏覽器的網址列是一致的,也就是說只需對網頁位址發送HTTP請求即可取得小說資訊。

20200505154724 Python 爬蟲 Aiohttp 爬各大小說網站排行榜

且排行榜的第一第二第三頁…都是有規則的,參數page代表分頁的頁數,第一頁預設不顯示。而URL位址根據頁數不同來顯示對應的小說資訊。

20200505155019 Python 爬蟲 Aiohttp 爬各大小說網站排行榜

從上述分析得知,只要動態改變URL位址的參數page即可獲得不同頁數的網頁內容,然後將網頁內容進行資料清洗並分析對應的小說資訊,最後將小說資訊寫入CSV檔案。

程式碼

getData()使用Aiohttp模組發送HTTP請求,參數url和headers分別代表請求位址和請求標頭,函數將回應內容作為傳回值;

savaData()是將回應內容進行資料清洗處理,從資料中分析小說資訊並寫入CSV檔案,參數result代表排行榜所有分頁的網頁內容。

run()是檢查 2 次來建置不同的URL位址,每次檢查是由Asyncio呼叫函數getData(),然後傳入目前的URL位址,產生工作物件task,每次檢查所產生的工作物件task都會寫入清單tasks,最後由Asyncio排程執行工作清單,將全部的執行結果以清單傳回,設定值給變數result;

執行函數__main__是定義請求標頭、格式化URL位址、建立get_event_loop物件和函數run(),這是為函數getData()和run()的變數進行初始化處理。

import asyncio
from aiohttp import ClientSession
from bs4 import BeautifulSoup
import csv
# 定義網站造訪函數 getData,將網站內容傳回
async def getData(url, headers):
    # 建立階段物件 session
    async with ClientSession() as session:
        # 發送GET請求,並設定請求標頭
        async with session.get(url,headers=headers) as response:
       # 傳回回應內容
            return await response.text()
def savaData(result):
    for i in result:
        soup = BeautifulSoup(i,'html.parser')
        find_div = soup.find_all('div',class_='book-mid-info')
        for d in find_div:
            name = d.find('h4').getText()
            author = d.find('a',class_='name').getText()
            update = d.find('p',class_='update').getText()
            # 寫入CSV檔案
            csvFile = open('data.csv','a',newline='',encoding="utf-8")
            writer = csv.writer(csvFile)
            writer.writerow([name,author,update])
            csvFile.close()
# 定義執行函數run
def run():
    for i in range(3):
     # 建置不同的URL位址並傳入函數getData,最後由asyncio模組執行
        task = asyncio.ensure_future(getData(url.format(i+1),headers))
     # 將所有請求加入到列表tasks
        tasks.append(task)
    # 等待所有請求執行完成,一併傳回全部的回應內容
    result = loop.run_until_complete(asyncio.gather(*tasks))
    savaData(result)
    print(len(result))
if __name__ == '__main__':
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36'
    }
    tasks = []
    url = "https://www.qidian.com/rank/hotsales?style=1&page={}"
    # 建立get_event_loop物件
    loop = asyncio.get_event_loop()
    # 呼叫函數run
    run()

1.若出現錯誤「PermissionError: [Errno 13] Permission denied: ‘data.csv’」請在執行python檔案的時候關閉目標CSV檔案,否則無法正常寫入。

2.寫入CSV檔案:csvFile = open(‘data.csv’,’a’,newline=”,encoding=’utf-8′) 這段後方一定要加上 encoding='utf-8' 若是不加上會出現 UnicodeEncodeError。

成果

20200505160826 Python 爬蟲 Aiohttp 爬各大小說網站排行榜

爬縱橫小說網

程式碼按前面改的,根據每個小說網各自的標籤更改程式碼即可。

縱橫小說網我選的是「完結榜」,網址為:http://www.zongheng.com/rank/details.html?rt=8&d=1&p=1

根據URL格式可以得知,p為page頁數的意思,因此只需要動態變動p=?即可。

20200506080646 Python 爬蟲 Aiohttp 爬各大小說網站排行榜

程式碼

跟起點小說網相比,較不同的地方在縱橫把小說名稱、作者…等資訊放在title屬性當中,因此要得到小說資訊不能使用getText(),而是使用[‘title’]抓取屬性內的值。

import asyncio
from aiohttp import ClientSession
from bs4 import BeautifulSoup
import csv
async def getData(url, headers):
    async with ClientSession() as session:
        async with session.get(url,headers=headers) as response:
            return await response.text()
def savaData(result):
    for i in result:
        soup = BeautifulSoup(i,'html.parser')
        find_div = soup.find_all('div',class_='rank_d_book_intro')
        for d in find_div:
            name = d.find('div',class_='rank_d_b_name')['title']
            author = d.find('div',class_='rank_d_b_cate')['title']
            update = d.find('span',class_='rank_d_b_time').getText()
            csvFile = open('data.csv','a',newline='',encoding="utf-8")
            writer = csv.writer(csvFile)
            writer.writerow([name,author,update])
            csvFile.close()
def run():
    for i in range(2):
        task = asyncio.ensure_future(getData(url.format(i+1),headers))
        tasks.append(task)
    result = loop.run_until_complete(asyncio.gather(*tasks))
    savaData(result)
    print(len(result))
if __name__ == '__main__':
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36'
    }
    tasks = []
    url = "http://www.zongheng.com/rank/details.html?rt=8&d=1&p={}"
    loop = asyncio.get_event_loop()
    run()

成果

20200506080931 Python 爬蟲 Aiohttp 爬各大小說網站排行榜

雖然Aiohttp的非同步平行處理可以加強爬蟲的效率,但是也會因為爬取速度過快而被網站判為爬蟲機器人,進一步引發一系列的反爬蟲機制。

4e52d54f6bc42abb41d26eb5b0df6517?s=250&d=wavatar&r=g Python 爬蟲 Aiohttp 爬各大小說網站排行榜
0 0 評分數
Article Rating
訂閱
通知
guest
0 Comments
在線反饋
查看所有評論