首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python Gevent 協程爬蟲:批量下載懶人圖庫 JS 特效模板

     編輯:更多程式設計知識

本文展示使用 Python gevent 協程庫實現高併發爬蟲,批量下載「懶人圖庫」網站的 JavaScript 特效模板壓縮包。包含完整程式碼、關鍵技術點解析與反爬蟲應對策略。

專案需求與架構

  • 目標:遍歷列表頁(共 48 頁),解析每個詳情頁連結,下載對應 ZIP 壓縮包。
  • 技術棧:Python 2.7、gevent(協程)、BeautifulSoup4(HTML 解析)、urllib(網路請求)。
  • 併發模型:gevent.monkey.patch_socket() 猴子補丁將標準庫阻塞 I/O 轉為協程非阻塞,單進程即可達成高併發。

完整爬蟲程式碼


#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib, os, sys
import gevent, re
from gevent import monkey
from bs4 import BeautifulSoup

gevent.monkey.patch_socket()  # 關鍵:必須在 import 其他網路模組前執行

HTTP_URL = 'https://www.lanrentuku.com%s'
DOWNLOAD_URL = 'https://www.lanrentuku.com/js/d%szip'
ID_REGEX = re.compile(r'\d{1,}.+')

def create_dir(filename):
    """建立下載目錄"""
    base = os.path.join(os.getcwd(), u'JS代碼模板')
    if not os.path.exists(base):
        os.makedirs(base)
    return os.path.join(base, filename)

def schedule(a, b, c):
    """下載進度顯示"""
    per = 100.0 * a * b / c
    if per > 100: per = 100
    sys.stdout.write('\r%.1f%%' % per)
    sys.stdout.flush()

def get_url_list(page_url):
    """解析列表頁,回傳 {詳情頁URL: 標題} 字典"""
    url_dict = {}
    try:
        html = urllib.urlopen(page_url).read()
        soup = BeautifulSoup(html, 'html.parser')
        div = soup.find('div', class_='list-pngjs')
        if not div: return url_dict
        for a_tag in div.find_all('a'):
            href = a_tag.get('href')
            title = a_tag.get_text(strip=True)
            if href and title:
                url_dict[HTTP_URL % href] = title
    except Exception as e:
        print(f"解析列表頁失敗 {page_url}: {e}")
    return url_dict

def download(detail_url, title):
    """下載單個 ZIP 檔案"""
    try:
        m = ID_REGEX.search(detail_url)
        if not m: return
        zip_url = DOWNLOAD_URL % m.group(0)
        filename = title + zip_url[-4:]  # 保留 .zip
        filepath = create_dir(filename)
        urllib.urlretrieve(zip_url, filepath, schedule)
        print(f"\n完成: {filename}")
    except Exception as e:
        print(f"\n下載失敗 {detail_url}: {e}")

def main():
    # 生成 1-48 頁列表頁 URL
    page_urls = ['https://www.lanrentuku.com/js/p%s.html' % i for i in range(1, 49)]
    
    jobs = []
    for page_url in page_urls:
        items = get_url_list(page_url)
        for detail_url, title in items.items():
            jobs.append(gevent.spawn(download, detail_url, title))
    
    print(f"共 {len(jobs)} 個下載任務,開始併發下載...")
    gevent.joinall(jobs)
    print("\n所有任務完成。")

if __name__ == '__main__':
    main()

關鍵技術點解析

1. Gevent 猴子補丁


from gevent import monkey
monkey.patch_socket()  # 或 monkey.patch_all()

必須在 import urllib、import requests 等網路模組之前執行,將底層 Socket 操作替換為非阻塞版本,使同步程式碼自動具備異步能力。

2. 協程池控制併發數

預設 gevent.joinall 會同時啟動所有綠色執行緒,可能導致連線數過多被封 IP。生產環境建議使用 gevent.pool.Pool 限制併發:


pool = gevent.pool.Pool(20)  # 限制同時 20 個協程
jobs = [pool.spawn(download, url, title) for url, title in items.items()]
gevent.joinall(jobs)

3. 反爬蟲應對

  • 設定合理的 User-Agent 模擬瀏覽器。
  • 下載間隔加入隨機延遲 gevent.sleep(random.uniform(0.5, 1.5))。
  • 捕獲 HTTP 403/429 錯誤並實施指數退避重試。

4. 進度顯示同步問題

多協程同時寫入 stdout 會導致進度條顯示混亂。解決方案:

  • 使用 threading.Lock 保護 sys.stdout.write。
  • 或改用 tqdm 等支援多執行緒的進度條庫。

Python 3 遷移提醒

  • urllib.urlopen → urllib.request.urlopen
  • urllib.urlretrieve → urllib.request.urlretrieve
  • xrange → range
  • print 語句 → print() 函式
  • BeautifulSoup(html) → BeautifulSoup(html, 'html.parser') 指定解析器
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved