**爬蟲,這個經常被人提到的詞,是對數據收集過程的一種形象化描述。特別是在Python語言中,由於其豐富的庫資源和良好的易用性,使得其成為編寫爬蟲的絕佳選擇。本文將從基礎知識開始,深入淺出地講解Python爬蟲的相關知識,並分享一些獨特的用法和實用技巧。本文將以實際的網站為例,深入闡述各個處理部分, ...
爬蟲,這個經常被人提到的詞,是對數據收集過程的一種形象化描述。特別是在Python語言中,由於其豐富的庫資源和良好的易用性,使得其成為編寫爬蟲的絕佳選擇。本文將從基礎知識開始,深入淺出地講解Python爬蟲的相關知識,並分享一些獨特的用法和實用技巧。本文將以實際的網站為例,深入闡述各個處理部分,並展示輸出,助力大家快速掌握Python爬蟲技巧。
開始之前:必要的庫
Python有很多庫可以用來編寫爬蟲,但我們這裡重點介紹兩個:requests和BeautifulSoup。
import requests
from bs4 import BeautifulSoup
requests
庫用於發送HTTP請求,而BeautifulSoup
庫則用於解析HTTP響應中的HTML。
基本爬蟲:爬取全部網頁內容
以Python官方網站(https://www.python.org/)為例,一個基本的Python爬蟲可能會這樣編寫:
url = "https://www.python.org/"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
print(soup.prettify()[:500])
這段代碼的目的是獲取網頁的內容,並使用BeautifulSoup庫進行解析。我們可以看到,requests.get(url)
是用來發送GET請求的,而BeautifulSoup(response.text, 'html.parser')
則是用來解析HTTP響應中的HTML內容的。
這段代碼的輸出前500個字元如下:
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 lt-ie8 lt-ie9"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--><html class="no-js" dir="ltr" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8"/>
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
<meta content="Python.org" name="application-name"/>
<meta content="The official home of the Python Programming Language"
使用CSS選擇器爬取特定元素
當我們希望獲取特定元素時,我們可以使用CSS選擇器。比如我們希望獲取Python官方網站中所有的頭部鏈接:
elements = soup.select('div.top-bar > ul > li > a')
for element in elements:
print(element.get('href'), element.text)
在這裡,div.top-bar > ul > li > a
是一個CSS選擇器,用來選擇
class為top-bar
的div元素下的ul元素中的li元素下的a元素。這些a元素就是我們想要的頭部鏈接。
這段代碼的部分輸出如下:
/ Python
/psf-landing/ PSF
/docs/ Docs
/pypl/ PyPI
/jobs/ Jobs
/community-landing/ Community
HTML解析語言爬取:XPath
除了CSS選擇器,還有一種常用的HTML解析技術是XPath。XPath,全稱XML Path Language,是一門在XML文檔中查找信息的語言,也可以用在HTML文檔解析中。
Python的lxml
庫提供了XPath的支持:
from lxml import etree
html = '<div><a href="/a">A</a><a href="/b">B</a></div>'
root = etree.HTML(html)
links = root.xpath('//a/@href')
print(links)
在這段代碼中,我們首先定義了一個HTML字元串。然後,我們使用etree.HTML()
函數將這個字元串解析成一個DOM樹。最後,我們使用root.xpath()
方法提取出所有的鏈接。
絕對鏈接爬取
你可能已經註意到,上述代碼的輸出中的鏈接是相對鏈接,而不是絕對鏈接。如果我們希望獲取絕對鏈接,我們可以使用urljoin
函數:
from urllib.parse import urljoin
elements = soup.select('div.top-bar > ul > li > a')
for element in elements:
absolute_url = urljoin(url, element.get('href'))
print(absolute_url, element.text)
這段代碼的部分輸出如下:
https://www.python.org/ Python
https://www.python.org/psf-landing/ PSF
https://www.python.org/docs/ Docs
https://www.python.org/pypl/ PyPI
https://www.python.org/jobs/ Jobs
https://www.python.org/community-landing/ Community
動態載入的數據爬取:Selenium
在許多現代的網頁中,數據可能不是在頁面載入時一次性載入的,而是通過JavaScript在用戶與頁面交互時動態載入的。這時,我們可能需要使用另一個工具:Selenium。
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('https://www.python.org/')
element = driver.find_element_by_css_selector('div.top-bar > ul > li > a')
print(element.text)
這段代碼使用Selenium模擬瀏覽器行為,獲取JavaScript動態載入的數據。在這個例子中,我們只獲取了第一個鏈接的文本,實際使用時,你可能需要根據需求進行更複雜的操作。
爬蟲代理
使用代理,可以幫助我們隱藏自己的真實IP地址,從而避免因爬取同一網站過多數據而被封IP。下麵是一段簡單的使用代理的代碼:
proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:1080",
}
response = requests.get("https://www.python.org/", proxies=proxies)
在這裡,我們定義了一個代理字典,並將其傳給requests.get()
函數。這樣,我們的請求就會通過代理伺服器發送,從而隱藏了我們的真實IP地址。
非同步爬蟲:提升爬蟲效率
在爬取大量數據時,我們通常需要進行多次HTTP請求,如果每次請求都等待前一次請求完成,那麼效率將會非常低。此時,我們可以使用Python的非同步IO庫asyncio
和aiohttp
來提高效率。下麵是一個簡單的例子:
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'http://python.org')
print(html[:500])
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
在這段代碼中,我們首先定義了一個非同步的fetch
函數,用於發送HTTP請求並獲取響應。然後,我們在main
函數中創建一個HTTP會話,並使用這個會話來發送請求。最後,我們使用事件迴圈來運行main
函數。
爬蟲框架:Scrapy
雖然使用上述方法可以實現爬蟲的基本功能,但在處理更複雜的爬蟲任務時,我們可能需要一個更強大的工具。Scrapy是一個用Python實現的強大的爬蟲框架,它為我們提供了許多高級功能,比如併發請求、數據處理和存儲等。
下麵是一個簡單的Scrapy爬蟲的例子:
import scrapy
class MySpider(scrapy.Spider):
name = 'myspider'
start_urls = ['http://python.org']
def parse(self, response):
self.log('Visited %s' % response.url)
yield {
'url': response.url,
'title': response.css('title::text').get(),
}
在這段代碼中,我們定義了一個繼承自scrapy.Spider
的爬蟲類。這個類中定義了爬蟲的名字、開始的URL和解析響應的方法。Scrapy將會自動為我們處理請求的發送和響應的接收,我們只需要關心如何從響應中提取數據即可。
自動化任務:定時爬蟲
有時我們需要定時執行爬蟲任務,比如每天爬取一次網站的數據。Python的schedule
庫可以幫助我們實現這一點:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).seconds.do(job)
while True:
schedule.run_pending()
time.sleep(1)
在這段代碼中,我們首先定義了一個爬蟲任務job
。然後,我們使用schedule.every().seconds.do()
方法設置任務的執行間隔。最後,我們使用一個無限迴圈來不斷執行待運行的任務。
爬蟲道德規範:遵守robots.txt
在進行爬蟲時,我們需要尊重網站的robots.txt
規則。robots.txt
是一個存放在網站根目錄下的文本文件,用於告訴爬蟲哪些頁面可以抓取,哪些頁面不可以抓取。
Python的urllib.robotparser
模塊可以幫助我們解析robots.txt
:
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.set_url('http://www.python.org/robots.txt')
rp.read()
can_fetch = rp.can_fetch('*', 'http://www.python.org/')
print(can_fetch)
在這段代碼中,我們首先創建了一個RobotFileParser
對象,然後使用set_url
方法設置robots.txt
的URL,並使用read
方法讀取和解析robots.txt
。最後,我們使用can_fetch
方法判斷我們的爬蟲是否可以抓取指定的URL。
請註意,不是所有的網站都有robots.txt
,也不是所有的網站都會嚴格遵守robots.txt
。在爬取網站時,除了尊重robots.txt
,我們還應該儘量減小爬蟲對網站的影響,例如限制爬取頻率,避免在網站高訪問量的時候爬取。
總結
總結起來,Python爬蟲雖然有許多複雜的技術和知識點,但只要掌握了基礎知識和一些實用技巧,就可以解決大部分的爬蟲任務。未來,我將繼續分享更多的Python爬蟲知識和技巧。
如有幫助,請多關註
個人微信公眾號:【Python全視角】
TeahLead_KrisChang,10+年的互聯網和人工智慧從業經驗,10年+技術和業務團隊管理經驗,同濟軟體工程本科,復旦工程管理碩士,阿裡雲認證雲服務資深架構師,上億營收AI產品業務負責人。