App下載

Python 網(wǎng)頁(yè)抓取教程 - 如何使用 Python 從任何網(wǎng)站抓取數(shù)據(jù)

被風(fēng)吹過(guò)灼思 2021-08-24 15:35:25 瀏覽數(shù) (6621)
反饋

網(wǎng)頁(yè)抓取是自動(dòng)從互聯(lián)網(wǎng)中提取特定數(shù)據(jù)的過(guò)程。它有許多用例,例如為機(jī)器學(xué)習(xí)項(xiàng)目獲取數(shù)據(jù)、創(chuàng)建價(jià)格比較工具或任何其他需要大量數(shù)據(jù)的創(chuàng)新想法。雖然理論上您可以手動(dòng)進(jìn)行數(shù)據(jù)提取,但互聯(lián)網(wǎng)的大量?jī)?nèi)容使這種方法在許多情況下不切實(shí)際。因此,知道如何構(gòu)建網(wǎng)絡(luò)爬蟲(chóng)可以派上用場(chǎng)。這篇文章的目的是教你如何用 Python 創(chuàng)建一個(gè)網(wǎng)頁(yè)爬蟲(chóng)。您將學(xué)習(xí)如何檢查網(wǎng)站以準(zhǔn)備抓取、使用 BeautifulSoup 提取特定數(shù)據(jù)、使用 Selenium 等待 JavaScript 渲染,以及將所有內(nèi)容保存在新的 JSON 或 CSV 文件中。

但首先,我應(yīng)該警告您網(wǎng)絡(luò)抓取的合法性。雖然抓取行為是合法的,但您可能提取的數(shù)據(jù)使用可能是非法的。確保你沒(méi)有爬?。?/p>

  • 受版權(quán)保護(hù)的內(nèi)容 – 由于它是某人的知識(shí)產(chǎn)權(quán),因此受法律保護(hù),您不能只是重復(fù)使用它。
  • 個(gè)人數(shù)據(jù)——如果您收集的信息可用于識(shí)別個(gè)人身份,則它被視為個(gè)人數(shù)據(jù),對(duì)于歐盟公民而言,它受 GDPR 保護(hù)。除非您有合法的理由來(lái)存儲(chǔ)這些數(shù)據(jù),否則最好完全跳過(guò)它。

一般來(lái)說(shuō),在抓取之前,您應(yīng)該始終閱讀網(wǎng)站的條款和條件,以確保您不會(huì)違反他們的政策。如果您不確定如何繼續(xù),請(qǐng)聯(lián)系網(wǎng)站所有者并征求同意。

您的Scraper需要什么?

要開(kāi)始構(gòu)建您自己的網(wǎng)絡(luò)爬蟲(chóng),您首先需要在您的機(jī)器上安裝Python。Ubuntu 20.04 和其他版本的 Linux 預(yù)裝了 Python 3。

要檢查您的設(shè)備上是否已經(jīng)安裝了 Python,請(qǐng)運(yùn)行以下命令:

python3 -v

如果您安裝了 Python,您應(yīng)該會(huì)收到類似如下輸出:

Python 3.8.2

此外,對(duì)于我們的網(wǎng)絡(luò)爬蟲(chóng),我們將使用 Python 包 BeautifulSoup(用于選擇特定數(shù)據(jù))和 Selenium(用于呈現(xiàn)動(dòng)態(tài)加載的內(nèi)容)。要安裝它們,只需運(yùn)行以下命令:

pip3 install beautifulsoup4

pip3 install selenium

最后一步是確保在您的機(jī)器上安裝了 Google Chrome和Chrome 驅(qū)動(dòng)程序。如果我們想使用 Selenium 抓取動(dòng)態(tài)加載的內(nèi)容,這些將是必要的。

 使用火狐瀏覽器或者其他瀏覽器也需要對(duì)應(yīng)的瀏覽器驅(qū)動(dòng)。

如何檢查頁(yè)面

現(xiàn)在你已經(jīng)安裝了所有東西,是時(shí)候開(kāi)始我們的抓取項(xiàng)目了。

您應(yīng)該根據(jù)需要選擇要抓取的網(wǎng)站。請(qǐng)記住,每個(gè)網(wǎng)站的內(nèi)容結(jié)構(gòu)都不同,因此當(dāng)您開(kāi)始自己抓取時(shí),您需要調(diào)整在此處學(xué)到的內(nèi)容。每個(gè)網(wǎng)站都需要對(duì)代碼進(jìn)行細(xì)微的更改。

對(duì)于本文,我決定從 IMDb 的前 250 部電影列表中抓取前十部電影的信息:https : //www.imdb.com/chart/top/。

首先,我們將獲得標(biāo)題,然后我們將通過(guò)從每部電影的頁(yè)面中提取信息來(lái)進(jìn)一步深入研究。一些數(shù)據(jù)將需要 JavaScript 呈現(xiàn)。

要開(kāi)始了解內(nèi)容的結(jié)構(gòu),您應(yīng)該右鍵單擊列表中的第一個(gè)標(biāo)題,然后選擇“檢查元素”。

頁(yè)面分析

通過(guò)按 CTRL+F 并在 HTML 代碼結(jié)構(gòu)中搜索,您將看到頁(yè)面上只有一個(gè)<table>標(biāo)記。這很有用,因?yàn)樗鼮槲覀兲峁┝擞嘘P(guān)如何訪問(wèn)數(shù)據(jù)的信息。

一個(gè) HTML 選擇器將為我們提供頁(yè)面中的所有標(biāo)題?table tbody tr td.titleColumn a?。那是因?yàn)樗袠?biāo)題都位于具有“?titleColumn?”類的表格單元格內(nèi)的錨點(diǎn)中。

使用這個(gè) CSS 選擇器并獲取每個(gè)錨點(diǎn)的?innerText?將為我們提供我們需要的標(biāo)題。您可以在剛剛打開(kāi)的新窗口中使用 JavaScript 行在瀏覽器控制臺(tái)中模擬:

document.querySelectorAll("table tbody tr td.titleColumn a")[0].innerText

你會(huì)看到這樣的結(jié)果:

現(xiàn)在我們有了這個(gè)選擇器,我們可以開(kāi)始編寫(xiě) Python 代碼并提取我們需要的信息。

如何使用 BeautifulSoup 提取靜態(tài)加載的內(nèi)容

我們列表中的電影標(biāo)題是靜態(tài)內(nèi)容。這是因?yàn)槿绻榭错?yè)面源代碼(頁(yè)面上的 CTRL+U 或右鍵單擊然后選擇查看頁(yè)面源代碼),您將看到標(biāo)題已經(jīng)存在。

靜態(tài)內(nèi)容通常更容易抓??取,因?yàn)樗恍枰?JavaScript 渲染。為了提取列表中的前十個(gè)標(biāo)題,我們將使用 BeautifulSoup 獲取內(nèi)容,然后將其打印在我們的Scraper的輸出中。

import requests
from bs4 import BeautifulSoup
 
page = requests.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
soup = BeautifulSoup(page.content, 'html.parser') # Parsing content using beautifulsoup
 
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    print(anchor.text) # Display the innerText of each anchor

上面的代碼使用我們?cè)诘谝徊街锌吹降倪x擇器從頁(yè)面中提取電影標(biāo)題錨點(diǎn)。然后循環(huán)遍歷前十個(gè)并顯示每個(gè)的innerText。

輸出應(yīng)如下所示:

如何提取動(dòng)態(tài)加載的內(nèi)容

隨著技術(shù)的進(jìn)步,網(wǎng)站開(kāi)始動(dòng)態(tài)加載其內(nèi)容。這提高了頁(yè)面的性能、用戶的體驗(yàn),甚至消除了爬蟲(chóng)的額外障礙。

但是,這使事情變得復(fù)雜,因?yàn)閺暮?jiǎn)單請(qǐng)求中檢索到的 HTML 將不包含動(dòng)態(tài)內(nèi)容。幸運(yùn)的是,有了Selenium,我們可以在瀏覽器中模擬一個(gè)請(qǐng)求,等待動(dòng)態(tài)內(nèi)容顯示出來(lái)。

如何使用 Selenium 進(jìn)行請(qǐng)求

您需要知道 chromedriver 的位置。以下代碼與第二步中的代碼相同,但這次我們使用 Selenium 發(fā)出請(qǐng)求。我們?nèi)匀粫?huì)像以前一樣使用 BeautifulSoup 解析頁(yè)面的內(nèi)容。

from bs4 import BeautifulSoup
from selenium import webdriver
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
driver.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup. Notice driver.page_source instead of page.content
 
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    print(anchor.text) # Display the innerText of each anchor

不要忘記將“YOUR-PATH-TO-CHROMEDRIVER”替換為您提取 chromedriver 的位置。此外,您應(yīng)該注意到page.content,當(dāng)我們創(chuàng)建 BeautifulSoup 對(duì)象時(shí),我們現(xiàn)在使用的是driver.page_source,它提供頁(yè)面的 HTML 內(nèi)容 。

如何使用 Selenium 提取靜態(tài)加載的內(nèi)容

使用上面的代碼,我們現(xiàn)在可以通過(guò)調(diào)用每個(gè)錨點(diǎn)上的 click 方法來(lái)訪問(wèn)每個(gè)電影頁(yè)面。

first_link = driver.find_elements_by_css_selector('table tbody tr td.titleColumn a')[0]
first_link.click()

這將模擬點(diǎn)擊第一部電影的鏈接。但是,在這種情況下,我建議您繼續(xù)使用driver.get instead. 這是因?yàn)閏lick()進(jìn)入不同頁(yè)面后您將無(wú)法再使用該方法,因?yàn)樾马?yè)面沒(méi)有指向其他九部電影的鏈接。

因此,單擊列表中的第一個(gè)標(biāo)題后,您需要返回第一頁(yè),然后單擊第二頁(yè),依此類推。這是對(duì)性能和時(shí)間的浪費(fèi)。相反,我們將只使用提取的鏈接并一一訪問(wèn)它們。

對(duì)于“肖申克的救贖”,電影頁(yè)面將是https://www.imdb.com/title/tt0111161/。我們將從頁(yè)面中提取電影的年份和時(shí)長(zhǎng),但這次我們將使用 Selenium 的函數(shù)而不是 BeautifulSoup 作為示例。在實(shí)踐中,您可以使用任何一種,因此請(qǐng)選擇您最喜歡的。

要檢索電影的年份和持續(xù)時(shí)間,您應(yīng)該重復(fù)我們?cè)陔娪绊?yè)面上執(zhí)行的第一步。

您會(huì)注意到您可以在帶有類?ipc-inline-list?(“?.ipc-inline-list?”選擇器)的第一個(gè)元素中找到所有信息,并且列表中的所有元素都有role屬性值presentation(?[role=’presentation’]?選擇器)。

from bs4 import BeautifulSoup
from selenium import webdriver
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
page = driver.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup
 
totalScrapedInfo = [] # In this list we will save all the information we scrape
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    driver.get('https://www.imdb.com/' + anchor['href']) # Access the movie’s page
    infolist = driver.find_elements_by_css_selector('.ipc-inline-list')[0] # Find the first element with class ‘ipc-inline-list’
    informations = infolist.find_elements_by_css_selector("[role='presentation']") # Find all elements with role=’presentation’ from the first element with class ‘ipc-inline-list’
    scrapedInfo = {
        "title": anchor.text,
        "year": informations[0].text,
        "duration": informations[2].text,
    } # Save all the scraped information in a dictionary
    totalScrapedInfo.append(scrapedInfo) # Append the dictionary to the totalScrapedInformation list
    
print(totalScrapedInfo) # Display the list with all the information we scraped

如何使用 Selenium 提取動(dòng)態(tài)加載的內(nèi)容

網(wǎng)絡(luò)抓取的下一個(gè)重要步驟是提取動(dòng)態(tài)加載的內(nèi)容。您可以在編輯列表部分的每個(gè)電影頁(yè)面(例如https://www.imdb.com/title/tt0111161/)上找到此類內(nèi)容。

如果您在頁(yè)面上使用檢查,您會(huì)看到您可以找到該部分作為屬性?data-testid?設(shè)置為的元素?firstListCardGroup-editorial?。但是如果你查看頁(yè)面源代碼,你不會(huì)在任何地方找到這個(gè)屬性值。這是因?yàn)榫庉嬃斜聿糠质怯?IMDB 動(dòng)態(tài)加載的。

在下面的示例中,我們將抓取每部電影的編輯列表,并將其添加到我們當(dāng)前的總抓取信息結(jié)果中。

為此,我們將導(dǎo)入更多包,以便等待我們的動(dòng)態(tài)內(nèi)容加載。

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
page = driver.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup
 
totalScrapedInfo = [] # In this list we will save all the information we scrape
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    driver.get('https://www.imdb.com/' + anchor['href']) # Access the movie’s page 
    infolist = driver.find_elements_by_css_selector('.ipc-inline-list')[0] # Find the first element with class ‘ipc-inline-list’
    informations = infolist.find_elements_by_css_selector("[role='presentation']") # Find all elements with role=’presentation’ from the first element with class ‘ipc-inline-list’
    scrapedInfo = {
        "title": anchor.text,
        "year": informations[0].text,
        "duration": informations[2].text,
    } # Save all the scraped information in a dictionary
    WebDriverWait(driver, 5).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='firstListCardGroup-editorial']")))  # We are waiting for 5 seconds for our element with the attribute data-testid set as `firstListCardGroup-editorial`
    listElements = driver.find_elements_by_css_selector("[data-testid='firstListCardGroup-editorial'] .listName") # Extracting the editorial lists elements
    listNames = [] # Creating an empty list and then appending only the elements texts
    for el in listElements:
        listNames.append(el.text)
    scrapedInfo['editorial-list'] = listNames # Adding the editorial list names to our scrapedInfo dictionary
    totalScrapedInfo.append(scrapedInfo) # Append the dictionary to the totalScrapedInformation list
    
print(totalScrapedInfo) # Display the list with all the information we scraped

對(duì)于前面的示例,您應(yīng)該獲得以下輸出:

如何保存抓取的內(nèi)容

現(xiàn)在我們擁有了所需的所有數(shù)據(jù),我們可以將其保存為 .json 或 .csv 文件,以便于閱讀。

為此,我們將只使用 Python 中的 JSON 和 CVS 包并將我們的內(nèi)容寫(xiě)入新文件:

import csv
import json
 
...
        
file = open('movies.json', mode='w', encoding='utf-8')
file.write(json.dumps(totalScrapedInfo))
 
writer = csv.writer(open("movies.csv", 'w'))
for movie in totalScrapedInfo:
    writer.writerow(movie.values())

抓取技巧和竅門

雖然到目前為止我們的指南已經(jīng)足夠先進(jìn),可以處理 JavaScript 渲染場(chǎng)景,但在 Selenium 中還有很多東西需要探索。

在本節(jié)中,我將分享一些可能會(huì)派上用場(chǎng)的提示和技巧。

1. 為您的請(qǐng)求計(jì)時(shí)

如果您在短時(shí)間內(nèi)向服務(wù)器發(fā)送數(shù)百個(gè)請(qǐng)求的垃圾郵件,很可能在某個(gè)時(shí)候會(huì)出現(xiàn)驗(yàn)證碼,或者您的 IP 甚至可能被阻止。不幸的是,Python 中沒(méi)有解決方法可以避免這種情況。

因此,您應(yīng)該在每個(gè)請(qǐng)求之間放置一些超時(shí)間隔,以便流量看起來(lái)更自然。

import time
import requests
 
page = requests.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
time.sleep(30) # Wait 30 seconds
page = requests.get('https://www.imdb.com/') # Getting page HTML through request

2. 錯(cuò)誤處理

由于網(wǎng)站是動(dòng)態(tài)的并且可以隨時(shí)更改結(jié)構(gòu),如果您經(jīng)常使用相同的網(wǎng)絡(luò)抓取工具,錯(cuò)誤處理可能會(huì)派上用場(chǎng)。

try:
    WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CSS_SELECTOR, "your selector")))
    break
except TimeoutException:
    # If the loading took too long, print message and try again
    print("Loading took too much time!")

當(dāng)您等待一個(gè)元素、提取它時(shí),甚至當(dāng)您只是發(fā)出請(qǐng)求時(shí),try and error 語(yǔ)法會(huì)很有用。

3. 截屏

如果您需要隨時(shí)獲取正在抓取的網(wǎng)頁(yè)的屏幕截圖,可以使用:

driver.save_screenshot(‘screenshot-file-name.png’)

這有助于在您處理動(dòng)態(tài)加載的內(nèi)容時(shí)進(jìn)行調(diào)試。

4. 閱讀文檔

最后但并非最不重要的一點(diǎn)是,不要忘記閱讀Selenium的文檔。該庫(kù)包含有關(guān)如何執(zhí)行您可以在瀏覽器中執(zhí)行的大多數(shù)操作的信息。

使用 Selenium,您可以填寫(xiě)表單、按下按鈕、回答彈出消息以及做許多其他很酷的事情。

如果您遇到新問(wèn)題,他們的文檔可能是您最好的朋友。

最后的想法

本文的目的是為您提供使用 Python 和 Selenium 和 BeautifulSoup 進(jìn)行網(wǎng)絡(luò)抓取的高級(jí)介紹。雖然這兩種技術(shù)仍有許多功能需要探索,但您現(xiàn)在已經(jīng)有了如何開(kāi)始抓取的堅(jiān)實(shí)基礎(chǔ)。

有時(shí)網(wǎng)頁(yè)抓取可能非常困難,因?yàn)榫W(wǎng)站開(kāi)始在開(kāi)發(fā)人員的道路上設(shè)置越來(lái)越多的障礙。其中一些障礙可能是驗(yàn)證碼、IP 塊或動(dòng)態(tài)內(nèi)容。僅使用 Python 和 Selenium 來(lái)克服它們可能很困難,甚至是不可能的。

所以,我也給你一個(gè)替代方案。嘗試使用Web 抓取 API來(lái)為您解決所有這些挑戰(zhàn)。它還使用輪換代理,因此您不必?fù)?dān)心在請(qǐng)求之間添加超時(shí)。請(qǐng)記住始終檢查您想要的數(shù)據(jù)是否可以合法提取和使用。


0 人點(diǎn)贊