使用Python怎么爬取網(wǎng)站

使用 Python怎么爬取網(wǎng)站,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

為宜昌等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及宜昌網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、宜昌網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

安裝依賴

我的 GitHub 存儲(chǔ)庫(kù) 中提供了本指南的所有資源。如果需要安裝 Python3 的幫助,請(qǐng)查看 Linux、Windows 和 Mac 的教程。

$ python3 -m venv$ source venv/bin/activate$ pip install requests bs4 pandas

如果你喜歡使用 JupyterLab ,則可以使用 notebook 運(yùn)行所有代碼。安裝 JupyterLab 有很多方法,這是其中一種:

# from the same virtual environment as above, run:$ pip install jupyterlab

為網(wǎng)站抓取項(xiàng)目設(shè)定目標(biāo)

現(xiàn)在我們已經(jīng)安裝了依賴項(xiàng),但是爬取網(wǎng)頁(yè)需要做什么?

讓我們退一步,確保使目標(biāo)清晰。下面是成功完成網(wǎng)頁(yè)爬取項(xiàng)目需求列表:

  • 我們收集的信息,是值得我們花大力氣去建立一個(gè)有效的網(wǎng)頁(yè)爬取器的。

  • 我們所下載的信息是可以通過網(wǎng)頁(yè)爬取器合法和道德地收集的。

  • 對(duì)如何在 HTML 代碼中找到目標(biāo)信息有一定的了解。

  • 利用恰當(dāng)?shù)墓ぞ撸涸诖饲闆r下,需要使用 BeautifulSoup 庫(kù)和 requests 庫(kù)。

  • 知道(或愿意去學(xué)習(xí))如何解析 JSON 對(duì)象。

  • 有足夠的 pandas 數(shù)據(jù)處理技能。

關(guān)于 HTML 的備注:HTML 是運(yùn)行在互聯(lián)網(wǎng)上的“猛獸”,但我們最需要了解的是標(biāo)簽的工作方式。標(biāo)簽是一對(duì)由尖括號(hào)包圍關(guān)鍵詞(一般成對(duì)出現(xiàn),其內(nèi)容在兩個(gè)標(biāo)簽中間)。比如,這是一個(gè)假裝的標(biāo)簽,稱為 pro-tip

<pro-tip> All you need to know about html is how tags work </pro-tip>

我們可以通過調(diào)用標(biāo)簽 pro-tip 來訪問其中的信息(All you need to know&hellip;)。本教程將進(jìn)一步介紹如何查找和訪問標(biāo)簽。要進(jìn)一步了解 HTML 基礎(chǔ)知識(shí),請(qǐng)查看 本文。

網(wǎng)站爬取項(xiàng)目中要找的是什么

有些數(shù)據(jù)利用網(wǎng)站爬取采集比利用其他方法更合適。以下是我認(rèn)為合適項(xiàng)目的準(zhǔn)則:

沒有可用于數(shù)據(jù)(處理)的公共 API。通過 API 抓取結(jié)構(gòu)化數(shù)據(jù)會(huì)容易得多,(所以沒有 API  )有助于澄清收集數(shù)據(jù)的合法性和道德性。而有相當(dāng)數(shù)量的結(jié)構(gòu)化數(shù)據(jù),并有規(guī)律的、可重復(fù)的格式,才能證明這種努力的合理性。網(wǎng)頁(yè)爬取可能會(huì)很痛苦。BeautifulSoup(bs4)使操作更容易,但無法避免網(wǎng)站的個(gè)別特殊性,需要進(jìn)行定制。數(shù)據(jù)的相同格式化不是必須的,但這確實(shí)使事情變得更容易。存在的  “邊際案例”(偏離規(guī)范)越多,爬取就越復(fù)雜。

免責(zé)聲明:我沒有參加過法律培訓(xùn);以下內(nèi)容無意作為正式的法律建議。

關(guān)于合法性,訪問大量有價(jià)值信息可能令人興奮,但僅僅因?yàn)樗强赡艿模⒉灰馕吨鴳?yīng)該這樣做。

值得慶幸的是,有一些公共信息可以指導(dǎo)我們的道德規(guī)范和網(wǎng)頁(yè)爬取工具。大多數(shù)網(wǎng)站都有與該網(wǎng)站關(guān)聯(lián)的 robots.txt 文件,指出允許哪些爬取活動(dòng),哪些不被允許。它主要用于與搜索引擎(網(wǎng)頁(yè)抓取工具的終極形態(tài))進(jìn)行交互。然而,網(wǎng)站上的許多信息都被視為公共信息。因此,有人將 robots.txt 文件視為一組建議,而不是具有法律約束力的文檔。 robots.txt 文件并不涉及數(shù)據(jù)的道德收集和使用等主題。

在開始爬取項(xiàng)目之前,問自己以下問題:

  • 我是否在爬取版權(quán)材料?

  • 我的爬取活動(dòng)會(huì)危害個(gè)人隱私嗎?

  • 我是否發(fā)送了大量可能會(huì)使服務(wù)器超載或損壞的請(qǐng)求?

  • 爬取是否會(huì)泄露出我不擁有的知識(shí)產(chǎn)權(quán)?

  • 是否有規(guī)范網(wǎng)站使用的服務(wù)條款,我是否遵循了這些條款?

  • 我的爬取活動(dòng)會(huì)減少原始數(shù)據(jù)的價(jià)值嗎?(例如,我是否打算按原樣重新打包數(shù)據(jù),或者可能從原始來源中抽取網(wǎng)站流量)?

當(dāng)我爬取一個(gè)網(wǎng)站時(shí),請(qǐng)確??梢詫?duì)所有這些問題回答 “否”。

要深入了解這些法律問題,請(qǐng)參閱 2018 年出版的 Krotov 和 Silva 撰寫的《Web 爬取的合法性和道德性》 和 Sellars 的《二十年 Web 爬取和計(jì)算機(jī)欺詐與濫用法案》。

現(xiàn)在開始爬取網(wǎng)站

經(jīng)過上述評(píng)估,我想出了一個(gè)項(xiàng)目。我的目標(biāo)是爬取愛達(dá)荷州所有 Family Dollar 商店的地址。 這些商店在農(nóng)村地區(qū)規(guī)模很大,因此我想了解有多少家這樣的商店。

起點(diǎn)是 Family Dollar 的位置頁(yè)面

使用 Python怎么爬取網(wǎng)站

愛達(dá)荷州 Family Dollar 所在地頁(yè)面

首先,讓我們?cè)?Python 虛擬環(huán)境中加載先決條件。此處的代碼將被添加到一個(gè) Python 文件(如果你想要個(gè)名稱,則為 scraper.py)或在 JupyterLab 的單元格中運(yùn)行。

import requests # for making standard html requestsfrom bs4 import BeautifulSoup # magical tool for parsing html dataimport json # for parsing datafrom pandas import DataFrame as df # premier library for data organization

接下來,我們從目標(biāo) URL 中請(qǐng)求數(shù)據(jù)。

page = requests.get("https://locations.familydollar.com/id/")soup = BeautifulSoup(page.text, 'html.parser')

BeautifulSoup 將 HTML 或 XML 內(nèi)容轉(zhuǎn)換為復(fù)雜樹對(duì)象。這是我們將使用的幾種常見對(duì)象類型。

  • BeautifulSoup &mdash;&mdash; 解析的內(nèi)容

  • Tag &mdash;&mdash; 標(biāo)準(zhǔn) HTML 標(biāo)記,這是你將遇到的 bs4 元素的主要類型

  • NavigableString &mdash;&mdash; 標(biāo)簽內(nèi)的文本字符串

  • Comment &mdash;&mdash; NavigableString 的一種特殊類型

當(dāng)我們查看 requests.get() 輸出時(shí),還有更多要考慮的問題。我僅使用 page.text() 將請(qǐng)求的頁(yè)面轉(zhuǎn)換為可讀的內(nèi)容,但是還有其他輸出類型:

  • page.text() 文本(最常見)

  • page.content() 逐字節(jié)輸出

  • page.json() JSON 對(duì)象

  • page.raw() 原始套接字響應(yīng)(對(duì)你沒啥用)

我只在使用拉丁字母的純英語(yǔ)網(wǎng)站上操作。 requests 中的默認(rèn)編碼設(shè)置可以很好地解決這一問題。然而,除了純英語(yǔ)網(wǎng)站之外,就是更大的互聯(lián)網(wǎng)世界。為了確保 requests 正確解析內(nèi)容,你可以設(shè)置文本的編碼:

page = requests.get(URL)page.encoding = 'ISO-885901'soup = BeautifulSoup(page.text, 'html.parser')

仔細(xì)研究 BeautifulSoup 標(biāo)簽,我們看到:

  • bs4 元素 tag 捕獲的是一個(gè) HTML 標(biāo)記。

  • 它具有名稱和屬性,可以像字典一樣訪問:tag['someAttribute']。

  • 如果標(biāo)簽具有相同名稱的多個(gè)屬性,則僅訪問第一個(gè)實(shí)例。

  • 可通過 tag.contents 訪問子標(biāo)簽。

  • 所有標(biāo)簽后代都可以通過 tag.contents 訪問。

  • 你始終可以使用以下字符串:re.compile("your_string") 訪問一個(gè)字符串的所有內(nèi)容,而不是瀏覽 HTML 樹。

確定如何提取相應(yīng)內(nèi)容

警告:此過程可能令人沮喪。

網(wǎng)站爬取過程中的提取可能是一個(gè)令人生畏的充滿了誤區(qū)的過程。我認(rèn)為解決此問題的最佳方法是從一個(gè)有代表性的示例開始然后進(jìn)行擴(kuò)展(此原理對(duì)于任何編程任務(wù)都是適用的)。查看頁(yè)面的 HTML 源代碼至關(guān)重要。有很多方法可以做到這一點(diǎn)。

你可以在終端中使用 Python 查看頁(yè)面的整個(gè)源代碼(不建議使用)。運(yùn)行此代碼需要你自擔(dān)風(fēng)險(xiǎn):

print(soup.prettify())

雖然打印出頁(yè)面的整個(gè)源代碼可能適用于某些教程中顯示的玩具示例,但大多數(shù)現(xiàn)代網(wǎng)站的頁(yè)面上都有大量?jī)?nèi)容。甚至 404 頁(yè)面也可能充滿了頁(yè)眉、頁(yè)腳等代碼。

通常,在你喜歡的瀏覽器中通過 “查看頁(yè)面源代碼” 來瀏覽源代碼是最容易的(單擊右鍵,然后選擇 “查看頁(yè)面源代碼” )。這是找到目標(biāo)內(nèi)容的最可靠方法(稍后我將解釋原因)。

使用 Python怎么爬取網(wǎng)站

Family Dollar 頁(yè)面源代碼

在這種情況下,我需要在這個(gè)巨大的 HTML 海洋中找到我的目標(biāo)內(nèi)容 &mdash;&mdash; 地址、城市、州和郵政編碼。通常,對(duì)頁(yè)面源(ctrl+F)的簡(jiǎn)單搜索就會(huì)得到目標(biāo)位置所在的位置。一旦我實(shí)際看到目標(biāo)內(nèi)容的示例(至少一個(gè)商店的地址),便會(huì)找到將該內(nèi)容與其他內(nèi)容區(qū)分開的屬性或標(biāo)簽。

首先,我需要在愛達(dá)荷州 Family Dollar 商店中收集不同城市的網(wǎng)址,并訪問這些網(wǎng)站以獲取地址信息。這些網(wǎng)址似乎都包含在 href 標(biāo)記中。太棒了!我將嘗試使用 find_all 命令進(jìn)行搜索:

dollar_tree_list = soup.find_all('href')dollar_tree_list

搜索 href 不會(huì)產(chǎn)生任何結(jié)果,該死。這可能是因?yàn)?nbsp;href 嵌套在 itemlist 類中而失敗。對(duì)于下一次嘗試,請(qǐng)搜索 item_list。由于 class 是 Python 中的保留字,因此使用 class_ 來作為替代。soup.find_all() 原來是 bs4 函數(shù)的瑞士軍刀。

dollar_tree_list = soup.find_all(class_ = 'itemlist')for i in dollar_tree_list[:2]:  print(i)

有趣的是,我發(fā)現(xiàn)搜索一個(gè)特定類的方法一般是一種成功的方法。通過找出對(duì)象的類型和長(zhǎng)度,我們可以了解更多有關(guān)對(duì)象的信息。

type(dollar_tree_list)len(dollar_tree_list)

可以使用 .contents 從 BeautifulSoup “結(jié)果集” 中提取內(nèi)容。這也是創(chuàng)建單個(gè)代表性示例的好時(shí)機(jī)。

example = dollar_tree_list[2] # a representative exampleexample_content = example.contentsprint(example_content)

使用 .attr 查找該對(duì)象內(nèi)容中存在的屬性。注意:.contents 通常會(huì)返回一個(gè)項(xiàng)目的精確的列表,因此第一步是使用方括號(hào)符號(hào)為該項(xiàng)目建立索引。

example_content = example.contents[0]example_content.attrs

現(xiàn)在,我可以看到 href 是一個(gè)屬性,可以像字典項(xiàng)一樣提取它:

example_href = example_content['href']print(example_href)

整合網(wǎng)站抓取工具

所有的這些探索為我們提供了前進(jìn)的路徑。這是厘清上面邏輯的一個(gè)清理版本。

city_hrefs = [] # initialise empty list for i in dollar_tree_list:    cont = i.contents[0]    href = cont['href']    city_hrefs.append(href) #  check to be sure all went wellfor i in city_hrefs[:2]:  print(i)

輸出的內(nèi)容是一個(gè)關(guān)于抓取愛達(dá)荷州 Family Dollar 商店 URL 的列表。

也就是說,我仍然沒有獲得地址信息!現(xiàn)在,需要抓取每個(gè)城市的 URL 以獲得此信息。因此,我們使用一個(gè)具有代表性的示例重新開始該過程。

page2 = requests.get(city_hrefs[2]) # again establish a representative examplesoup2 = BeautifulSoup(page2.text, 'html.parser')

使用 Python怎么爬取網(wǎng)站

Family Dollar 地圖和代碼

地址信息嵌套在 type="application/ld+json" 里。經(jīng)過大量的地理位置抓取之后,我開始認(rèn)識(shí)到這是用于存儲(chǔ)地址信息的一般結(jié)構(gòu)。幸運(yùn)的是,soup.find_all() 開啟了利用 type 搜索。

arco = soup2.find_all(type="application/ld+json")print(arco[1])

地址信息在第二個(gè)列表成員中!原來如此!

使用 .contents 提?。◤牡诙€(gè)列表項(xiàng)中)內(nèi)容(這是過濾后的合適的默認(rèn)操作)。同樣,由于輸出的內(nèi)容是一個(gè)列表,因此我為該列表項(xiàng)建立了索引:

arco_contents = arco[1].contents[0]arco_contents

喔,看起來不錯(cuò)。此處提供的格式與 JSON 格式一致(而且,該類型的名稱中確實(shí)包含 “json”)。 JSON  對(duì)象的行為就像是帶有嵌套字典的字典。一旦你熟悉利用其去工作,它實(shí)際上是一種不錯(cuò)的格式(當(dāng)然,它比一長(zhǎng)串正則表達(dá)式命令更容易編程)。盡管從結(jié)構(gòu)上看起來像一個(gè)  JSON 對(duì)象,但它仍然是 bs4 對(duì)象,需要通過編程方式轉(zhuǎn)換為 JSON 對(duì)象才能對(duì)其進(jìn)行訪問:

arco_json =  json.loads(arco_contents)
type(arco_json)print(arco_json)

在該內(nèi)容中,有一個(gè)被調(diào)用的 address 鍵,該鍵要求地址信息在一個(gè)比較小的嵌套字典里??梢赃@樣檢索:

arco_address = arco_json['address']arco_address

好吧,請(qǐng)大家注意。現(xiàn)在我可以遍歷存儲(chǔ)愛達(dá)荷州 URL 的列表:

locs_dict = [] # initialise empty list for link in city_hrefs:  locpage = requests.get(link)   # request page info  locsoup = BeautifulSoup(locpage.text, 'html.parser')      # parse the page's content  locinfo = locsoup.find_all(type="application/ld+json")      # extract specific element  loccont = locinfo[1].contents[0]        # get contents from the bs4 element set  locjson = json.loads(loccont)  # convert to json  locaddr = locjson['address'] # get address  locs_dict.append(locaddr) # add address to list

用 Pandas 整理我們的網(wǎng)站抓取結(jié)果

我們?cè)谧值渲醒b載了大量數(shù)據(jù),但是還有一些額外的無用項(xiàng),它們會(huì)使重用數(shù)據(jù)變得比需要的更為復(fù)雜。要執(zhí)行最終的數(shù)據(jù)組織,我們需要將其轉(zhuǎn)換為 Pandas 數(shù)據(jù)框架,刪除不需要的列 @type 和 country,并檢查前五行以確保一切正常。

locs_df = df.from_records(locs_dict)locs_df.drop(['@type', 'addressCountry'], axis = 1, inplace = True)locs_df.head(n = 5)

確保保存結(jié)果?。?/p>

df.to_csv(locs_df, "family_dollar_ID_locations.csv", sep = ",", index = False)

我們做到了!所有愛達(dá)荷州 Family Dollar 商店都有一個(gè)用逗號(hào)分隔的列表。多令人興奮。

Selenium 和數(shù)據(jù)抓取的一點(diǎn)說明

Selenium 是用于與網(wǎng)頁(yè)自動(dòng)交互的常用工具。為了解釋為什么有時(shí)必須使用它,讓我們來看一個(gè)使用 Walgreens 網(wǎng)站的示例。 “檢查元素” 提供了瀏覽器顯示內(nèi)容的代碼:

使用 Python怎么爬取網(wǎng)站

Walgreens 位置頁(yè)面和代碼

雖然 “查看頁(yè)面源代碼” 提供了有關(guān) requests 將獲得什么內(nèi)容的代碼:

使用 Python怎么爬取網(wǎng)站

Walgreens 源代碼

如果這兩個(gè)不一致,是有一些插件可以修改源代碼 &mdash;&mdash; 因此,應(yīng)在將頁(yè)面加載到瀏覽器后對(duì)其進(jìn)行訪問。requests 不能做到這一點(diǎn),但是 Selenium 可以做到。

Selenium 需要 Web 驅(qū)動(dòng)程序來檢索內(nèi)容。實(shí)際上,它會(huì)打開 Web 瀏覽器,并收集此頁(yè)面的內(nèi)容。Selenium 功能強(qiáng)大 &mdash;&mdash;  它可以通過多種方式與加載的內(nèi)容進(jìn)行交互(請(qǐng)閱讀文檔)。使用 Selenium 獲取數(shù)據(jù)后,繼續(xù)像以前一樣使用 BeautifulSoup:

url = "https://www.walgreens.com/storelistings/storesbycity.jsp?requestType=locator&state=ID"driver = webdriver.Firefox(executable_path = 'mypath/geckodriver.exe')driver.get(url)soup_ID = BeautifulSoup(driver.page_source, 'html.parser')store_link_soup = soup_ID.find_all(class_ = 'col-xl-4 col-lg-4 col-md-4')

對(duì)于 Family Dollar 這種情形,我不需要 Selenium,但是當(dāng)呈現(xiàn)的內(nèi)容與源代碼不同時(shí),我確實(shí)會(huì)保留使用 Selenium。

小結(jié)

總之,使用網(wǎng)站抓取來完成有意義的任務(wù)時(shí):

  • 耐心一點(diǎn)

  • 查閱手冊(cè)(它們非常有幫助)

如果你對(duì)答案感到好奇:

使用 Python怎么爬取網(wǎng)站

Family Dollar 位置圖

美國(guó)有很多 Family Dollar 商店。

完整的源代碼是:

import requestsfrom bs4 import BeautifulSoupimport jsonfrom pandas import DataFrame as df page = requests.get("https://www.familydollar.com/locations/")soup = BeautifulSoup(page.text, 'html.parser') # find all state linksstate_list = soup.find_all(class_ = 'itemlist') state_links = [] for i in state_list:    cont = i.contents[0]    attr = cont.attrs    hrefs = attr['href']    state_links.append(hrefs) # find all city linkscity_links = [] for link in state_links:    page = requests.get(link)    soup = BeautifulSoup(page.text, 'html.parser')    familydollar_list = soup.find_all(class_ = 'itemlist')    for store in familydollar_list:        cont = store.contents[0]        attr = cont.attrs        city_hrefs = attr['href']        city_links.append(city_hrefs)# to get individual store linksstore_links = [] for link in city_links:    locpage = requests.get(link)    locsoup = BeautifulSoup(locpage.text, 'html.parser')    locinfo = locsoup.find_all(type="application/ld+json")    for i in locinfo:        loccont = i.contents[0]        locjson = json.loads(loccont)        try:            store_url = locjson['url']            store_links.append(store_url)        except:            pass # get address and geolocation informationstores = [] for store in store_links:    storepage = requests.get(store)    storesoup = BeautifulSoup(storepage.text, 'html.parser')    storeinfo = storesoup.find_all(type="application/ld+json")    for i in storeinfo:        storecont = i.contents[0]        storejson = json.loads(storecont)        try:            store_addr = storejson['address']            store_addr.update(storejson['geo'])            stores.append(store_addr)        except:            pass # final data parsingstores_df = df.from_records(stores)stores_df.drop(['@type', 'addressCountry'], axis = 1, inplace = True)stores_df['Store'] = "Family Dollar" df.to_csv(stores_df, "family_dollar_locations.csv", sep = ",", index = False)

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。

新聞名稱:使用Python怎么爬取網(wǎng)站
URL標(biāo)題:http://bm7419.com/article20/ijhgco.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)頁(yè)設(shè)計(jì)公司、軟件開發(fā)、搜索引擎優(yōu)化、外貿(mào)網(wǎng)站建設(shè)、品牌網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)