跳到主要內容

[Python爬蟲教學]非同步網頁爬蟲使用GRequests套件提升爬取效率的實作技巧

python_asynchronous_scraper_using_grequests
Photo by Zan on Unsplash

網頁相信是許多人取得訊息的主要來源,為了追蹤、分析或取得想要的資訊,Python網頁爬蟲技術廣泛的使用,而如果所要爬取的資料量非常大時,效能就會變得非常的重要,而非同步(Asynchronous)處理則是用來提升效率的常用方法。

因此,本文想要以104人力銀行網站,爬取Python職缺為例,來和讀者分享如何使用GRequests套件,來開發Python非同步網頁爬蟲,並且比較同步與非同步網頁爬蟲所花費的執行時間,其中的重點包含:

  • 同步vs非同步
  • GRequests套件
  • Python同步網頁爬蟲
  • Python非同步網頁爬蟲

一、同步vs非同步

簡單來說,同步(Synchronous)就是接到一個任務後,需要等到它完成,才能繼續執行下一個任務。就像到了中午買午餐時,想要買一個炒飯和7-11的飲料,同步(Synchronous)處理就是需要等到炒飯做完了,才能去7-11買飲料,導致花費較多的執行時間。

而非同步(Asynchronous)則是能夠平行處理,無需等待第一個任務完成,即可執行其它的任務,只要第一個任務完成了,再回來處理。以上面的例子來說,非同步(Asynchronous)處理就是在叫完炒飯的同時,就可以去7-11買飲料,達到同時處理多個任務,提升執行效率。

二、GRequests套件

一般的情況下,使用Python開發網頁爬蟲時,所使用的requests套件,就是同步(Synchronous)處理,在發送一個請求(request)後,在還沒有收到回應前,不會執行其它的任務。

而grequests套件則是封裝了requests及gevent(協程)的套件,只要建置網址請求清單,就能夠平行發送多個請求(request),達到非同步(Asynchronous)處理,提升Python網頁爬蟲的執行效率。

三、Python同步網頁爬蟲

本文以Visual Studio Code為例,開啟Python網頁爬蟲專案,在Terminal視窗中,利用以下的指令來安裝所需的套件

$ pip install beautifulsoup4

$ pip install requests

$ pip install lxml  #HTML解析器

接著,新增synch.py檔案,並且引用以下的模組(Module)

from bs4 import BeautifulSoup
import requests
import time  

前往104人力銀行網站,在搜尋的地方輸入python進行查詢後,來分析一下所要爬取的職缺網頁結構,如下圖:

python_asynchronous_scraper_using_grequests

開啟synch.py檔案,就可以利用以下的範例,爬取104人力銀行網站1到10頁的Python職缺

from bs4 import BeautifulSoup
import requests
import time


start_time = time.time() #開始時間

for page in range(1, 11):  #爬取1-10頁

    response = requests.get(
        "https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=" + str(page) + "&jobsource=2018indexpoc&ro=0")
    soup = BeautifulSoup(response.content, "lxml")  #解析HTML原始碼

    blocks = soup.find_all("div", {"class": "b-block__left"})  #職缺區塊
    for block in blocks:

        job = block.find("a", {"class": "js-job-link"})  #職缺名稱
        if job is None:
            continue

        company = block.find_all("li")[1]  #公司名稱
        salary = block.find("span", {"class": "b-tag--default"})  #待遇

        print((job.getText(),) + (company.getText().strip(),) + (salary.getText(),))

print("花費:" + str(time.time() - start_time) + "秒")

執行結果

python_asynchronous_scraper_using_grequests
範例中第6行,利用time模組(Module)取得開始執行的時間,接著,透過迴圈爬取1到10頁的Python職缺後,第26行計算總共花費多少時間。而從執行結果可以看到Python同步網頁爬蟲約花費5秒的時間。

四、Python非同步網頁爬蟲

接下來,另外新增asynch.py檔案,開發Python非同步網頁爬蟲。首先,利用以下的指令來安裝GRequests套件

$ pip install grequests

開啟asynch.py檔案,引用以下的模組(Module)

from bs4 import BeautifulSoup
import grequests
import time

要使用GRequests模組(Module)來平行發送請求(request),需先建立請求網址清單,如下範例

from bs4 import BeautifulSoup
import grequests
import time


start_time = time.time()  #開始時間

links = list()  #請求網址清單(1-10頁的網址)
for page in range(1, 11):
    links.append("https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=" +
                 str(page) + "&jobsource=2018indexpoc&ro=0")

以上範例的第8~11行所做的事情等同於以下的範例

links = [
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=1&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=2&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=3&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=4&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=5&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=6&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=7&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=8&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=9&jobsource=2018indexpoc&ro=0
	https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=10&jobsource=2018indexpoc&ro=0
]

有了請求網址清單後,就可以利用GRequests模組(Module)建立請求清單集合,並且透過imap()方法(Method)平行發送請求,如下範例:

from bs4 import BeautifulSoup
import grequests
import time


start_time = time.time()  #開始時間

links = list()  # 請求網址清單(1-10頁的網址)
for page in range(1, 11):
    links.append("https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=" +
                 str(page) + "&jobsource=2018indexpoc&ro=0")

reqs = (grequests.get(link) for link in links)  # 建立請求集合
response = grequests.imap(reqs, grequests.Pool(10))  # 平行發送請求

其中,Pool為處理池,由於有十個網頁,所以設定為10。有了十個網頁的回應結果後,就可以透過迴圈來進行職缺爬取的動作,如下範例:

from bs4 import BeautifulSoup
import grequests
import time


start_time = time.time()  #開始時間

links = list()  # 請求網址清單(1-10頁的網址)
for page in range(1, 11):
    links.append("https://www.104.com.tw/jobs/search/?keyword=python&order=1&page=" +
                 str(page) + "&jobsource=2018indexpoc&ro=0")

reqs = (grequests.get(link) for link in links)  # 建立請求集合
response = grequests.imap(reqs, grequests.Pool(4))  # 發送請求

for r in response:
    soup = BeautifulSoup(r.content, "lxml")  # 解析HTML原始碼

    blocks = soup.find_all("div", {"class": "b-block__left"})  # 職缺區塊
    for block in blocks:

        job = block.find("a", {"class": "js-job-link"})  # 職缺名稱
        if job is None:
            continue

        company = block.find_all("li")[1]  # 公司名稱
        salary = block.find("span", {"class": "b-tag--default"})  # 待遇

        print((job.getText(),) + (company.getText().strip(),) + (salary.getText(),))

print("花費:" + str(time.time() - start_time) + "秒")

執行結果

python_asynchronous_scraper_using_grequests

從執行結果可以看到,Python非同步網頁爬蟲所花費的時間較同步網頁爬蟲快了近3秒的時間,當所要爬取的資料量越大時,相差的執行時間就會更加明顯。

五、小結

本文利用GRequests套件,非同步的發送請求(request)及接收回應結果,來達到Python非同步網頁爬蟲的效果,進而提升執行的效率。當然,除了GRequests套件外,還有其他像asyncio等套件,提供更多的附加功能,來實作更複雜的非同步網頁爬蟲。

您也有使用Python網頁爬蟲來取得大量的網頁資料嗎?可以參考本文所分享的非同步網頁爬蟲實作技巧,來提升爬取的效率唷。

如果您喜歡我的文章,請幫我按五下Like(使用GoogleFacebook帳號免費註冊),支持我創作教學文章,回饋由LikeCoin基金會出資,完全不會花到錢,感謝大家。


有想要看的教學內容嗎?歡迎利用以下的Google表單讓我知道,將有機會成為教學文章,分享給大家😊

Python學習資源
Python網頁爬蟲推薦課程
    Python網頁爬蟲-BeautifulSoup教學
    Python網頁爬蟲-Selenium教學
    Python非同步網頁爬蟲
    Python網頁爬蟲應用
    Python網頁爬蟲部署
    Python網頁爬蟲資料儲存
    Python網頁爬蟲技巧









    留言