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

Python 爬蟲入門:使用 urllib 抓取網頁內容完整教學

     編輯:更多程式設計知識

網頁抓取是資料收集、監控、自動化的基礎技能。Python 標準庫 urllib 提供無第三方依賴的 HTTP 客戶端功能。本文詳細說明 urllib.request 核心用法、錯誤處理、標頭偽裝、串流下載、編碼處理與最佳實務,並對比 requests 庫優劣。

基礎 GET 請求


import urllib.request
import urllib.error

url = "https://www.baidu.com"

# 簡單請求
try:
    response = urllib.request.urlopen(url)
    html = response.read().decode('utf-8', errors='ignore')
    print(f"Status: {response.status}")
    print(f"Length: {len(html)}")
except urllib.error.URLError as e:
    print(f"網路錯誤: {e.reason}")
except urllib.error.HTTPError as e:
    print(f"HTTP 錯誤: {e.code} {e.reason}")

進階 Request 物件:標頭、方法、資料


# 建立 Request 物件(支援自訂標頭、POST 資料等)
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept': 'text/html,application/xhtml+xml',
    'Accept-Language': 'zh-TW,zh;q=0.9,en;q=0.8',
}
req = urllib.request.Request(url, headers=headers)

try:
    with urllib.request.urlopen(req, timeout=10) as response:
        html = response.read().decode('utf-8')
        print(response.headers.get('Content-Type'))
except Exception as e:
    print(f"請求失敗: {e}")

POST 請求與表單資料


import urllib.parse

post_url = "https://httpbin.org/post"
data = urllib.parse.urlencode({'key1': 'value1', 'key2': 'value2'}).encode('utf-8')
req = urllib.request.Request(post_url, data=data, method='POST')
req.add_header('Content-Type', 'application/x-www-form-urlencoded')

with urllib.request.urlopen(req) as response:
    print(response.read().decode())

串流下載大檔案(避免記憶體爆炸)


def download_file(url, filepath, chunk_size=8192):
    """串流下載,支援大檔案"""
    req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
    try:
        with urllib.request.urlopen(req, timeout=30) as response, \
             open(filepath, 'wb') as f:
            total = response.length or 0
            downloaded = 0
            while True:
                chunk = response.read(chunk_size)
                if not chunk:
                    break
                f.write(chunk)
                downloaded += len(chunk)
                if total:
                    print(f"\r下載進度: {downloaded}/{total} ({downloaded*100/total:.1f}%)", end="")
            print("\n下載完成")
    except Exception as e:
        print(f"下載失敗: {e}")

download_file("https://speedtest.ftp.otenet.gr/files/test1Mb.db", "test1Mb.db")

編碼自動偵測與處理


import chardet  # pip install chardet

def fetch_with_encoding(url):
    req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
    with urllib.request.urlopen(req) as response:
        raw = response.read()
        # 1. 優先用 HTTP 標頭 charset
        content_type = response.headers.get('Content-Type', '')
        encoding = None
        if 'charset=' in content_type:
            encoding = content_type.split('charset=')[-1].split(';')[0].strip()
        # 2. 次優先用 chardet 偵測
        if not encoding:
            detected = chardet.detect(raw)
            encoding = detected['encoding']
            confidence = detected['confidence']
            print(f"偵測編碼: {encoding} (信心度: {confidence})")
        # 3. 最後 fallback utf-8
        encoding = encoding or 'utf-8'
        return raw.decode(encoding, errors='replace')

html = fetch_with_encoding("https://example.com")

錯誤處理完整架構


import socket
from urllib.error import URLError, HTTPError

def robust_fetch(url, retries=3, timeout=10):
    for attempt in range(retries):
        try:
            req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
            with urllib.request.urlopen(req, timeout=timeout) as response:
                return response.read().decode('utf-8', errors='ignore')
        except HTTPError as e:
            if e.code in (429, 500, 502, 503, 504):
                print(f"HTTP {e.code}, 重試 {attempt+1}/{retries}")
                time.sleep(2 ** attempt)  # 指數退避
            else:
                raise
        except URLError as e:
            print(f"網路錯誤: {e.reason}, 重試 {attempt+1}/{retries}")
            time.sleep(2 ** attempt)
        except socket.timeout:
            print(f"逾時, 重試 {attempt+1}/{retries}")
            time.sleep(2 ** attempt)
    raise Exception(f"重試 {retries} 次失敗: {url}")

Cookie 處理與 Session 維持


import http.cookiejar

# 建立 Cookie 容器
cookie_jar = http.cookiejar.CookieJar()
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie_jar))
urllib.request.install_opener(opener)  # 全域生效

# 登入
login_data = urllib.parse.urlencode({'username': 'user', 'password': 'pass'}).encode()
urllib.request.urlopen('https://example.com/login', login_data)

# 後續請求自動攜帶 Cookie
response = urllib.request.urlopen('https://example.com/dashboard')
print(response.read().decode())

urllib vs requests 對比

特性urllib (標準庫)requests (第三方)
安裝無需安裝pip install requests
API 設計較冗長、物件導向簡潔、人性化
自動編碼偵測需手動處理內建 .encoding、.text
Session/Cookie手動建立 Openerrequests.Session() 自動
JSON 支援手動 json.loads.json() 直接回傳 dict
串流下載支援支援 stream=True
代理、認證、重試手動實作內建/擴充套件豐富
效能略快(較輕量)略慢(功能豐富)

建議: 簡單腳本、無依賴環境用 urllib;專案開發、複雜爬蟲、需維護性用 requests。

反爬蟲基礎應對


# 1. 旋轉 User-Agent
USER_AGENTS = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...',
    # 更多...
]
headers = {'User-Agent': random.choice(USER_AGENTS)}

# 2. 適當延遲
time.sleep(random.uniform(1, 3))

# 3. 處理重導向、Referer
req.add_header('Referer', 'https://www.google.com/')

# 4. 使用代理 IP(進階)
proxy = urllib.request.ProxyHandler({'http': 'https://proxy_ip:port'})
opener = urllib.request.build_opener(proxy)

總結

urllib 是 Python 標準庫 HTTP 客戶端,功能完整但 API 較繁瑣。掌握 Request 物件、串流下載、編碼偵測、錯誤重試、Cookie 維持等核心技巧,能無第三方依賴完成大多數抓取任務。實際專案建議搭配 requests + BeautifulSoup/lxml + selenium/playwright 構建完整爬蟲生態。遵守 robots.txt、控制請求頻率、處理反爬機制,是負責任爬蟲開發者的基本素養。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved