网站内容抓取工具(有人将robots.txt文件视为一组建议.py文件)
优采云 发布时间: 2021-11-04 17:22网站内容抓取工具(有人将robots.txt文件视为一组建议.py文件)
关于合法性,获得大量有价值的信息可能令人兴奋,但仅仅因为它是可能的并不意味着应该这样做。
幸运的是,有一些公共信息可以指导我们的道德和网络抓取工具。大多数网站都有一个与网站相关联的robots.txt文件,指明哪些爬行活动是允许的,哪些是不允许的。它主要用于与搜索引擎交互(网页抓取工具的终极形式)。但是,网站 上的大部分信息都被视为公开信息。因此,有些人将 robots.txt 文件视为一组建议,而不是具有法律约束力的文件。robots.txt 文件不涉及道德采集和数据使用等主题。
在开始抓取项目之前,先问自己以下问题:
当我抓取 网站 时,请确保您可以对所有这些问题回答“否”。
要了解有关这些法律问题的更多信息,请参阅 Krotov 和 Silva 于 2018 年出版的“网络爬虫的合法性和道德”以及 Sellars 的“网络爬虫二十年和计算机欺诈和滥用法案”。
现在开始爬取网站
经过上面的评估,我想出了一个项目。我的目标是抓取爱达荷州所有 Family Dollar 商店的地址。这些店在农村很大,所以我想知道有多少这样的店。
起点是Family Dollar的位置页面
Family Dollar,爱达荷州位置页面
首先,让我们在 Python 虚拟环境中加载先决条件。此处的代码将添加到 Python 文件(如果需要名称,则为 scraper.py)或在 JupyterLab 的单元格中运行。
import requests # for making standard html requests
from bs4 import BeautifulSoup # magical tool for parsing html data
import json # for parsing data
from pandas import DataFrame as df # premier library for data organization
接下来,我们从目标 URL 请求数据。
page = requests.get("https://locations.familydollar.com/id/")
soup = BeautifulSoup(page.text, 'html.parser')
BeautifulSoup 将 HTML 或 XML 内容转换为复杂的树对象。这些是我们将使用的几种常见对象类型。
当我们查看 requests.get() 的输出时,还有更多问题需要考虑。我只使用 page.text() 将请求的页面转换为可读内容,但还有其他输出类型:
我只对使用拉丁字母的纯英语 网站 进行操作。requests中的默认编码设置可以很好的解决这个问题。不过,除了纯英文的网站,就是更大的互联网世界。为确保请求正确解析内容,您可以设置文本的编码:
page = requests.get(URL)
page.encoding = 'ISO-885901'
soup = BeautifulSoup(page.text, 'html.parser')
仔细观察 BeautifulSoup 标签,我们看到:
确定如何提取内容
警告:此过程可能令人沮丧。
网站 爬取过程中的提取可能是一个充满误解的艰巨过程。我认为解决这个问题最好的方法是从一个有代表性的例子开始,然后再扩展(这个原则适用于任何编程任务)。查看页面的 HTML 源代码很重要。有很多方法可以做到这一点。
您可以在终端中使用 Python 来查看页面的整个源代码(不推荐)。运行此代码风险自负:
print(soup.prettify())
虽然打印页面的整个源代码可能适合一些教程中展示的玩具示例,但大多数现代 网站 页面都有很多内容。甚至 404 页面也可能充满了页眉、页脚和其他代码。
通常,在您喜欢的浏览器中通过“查看页面源代码”来浏览源代码是最容易的(右键单击并选择“查看页面源代码”)。这是找到目标内容最可靠的方式(我稍后会解释原因)。
家庭美元页面源代码
在这种情况下,我需要在这个巨大的 HTML 海洋中找到我的目标内容地址、城市、州和邮政编码。通常,在页面源上进行简单的搜索(ctrl+F)就会得到目标位置的位置。一旦我真正看到目标内容的示例(至少是一家商店的地址),我就会找到将该内容与其他内容区分开来的属性或标签。
首先,我需要在爱达荷州的Family Dollar商店采集不同城市的URL,并访问这些网站以获取地址信息。这些 URL 似乎收录在 href 标签中。奇妙!我将尝试使用 find_all 命令进行搜索:
dollar_tree_list = soup.find_all('href')
dollar_tree_list
搜索 href 不会产生任何结果,该死的。这可能会失败,因为 href 嵌套在 itemlist 类中。对于下一次尝试,搜索 item_list。由于 class 是 Python 中的保留字,因此使用 class_ 代替。sound.find_all() 原来是 bs4 函数的瑞士*敏*感*词*。
dollar_tree_list = soup.find_all(class_ = 'itemlist')
for i in dollar_tree_list[:2]:
print(i)
有趣的是,我发现搜索特定类的方法通常是成功的方法。通过找出对象的类型和长度,我们可以了解更多关于对象的信息。
type(dollar_tree_list)
len(dollar_tree_list)
您可以使用 .contents 从 BeautifulSoup“结果集”中提取内容。这也是创建单个代表性示例的好时机。
example = dollar_tree_list[2] # a representative example
example_content = example.contents
print(example_content)
使用 .attr 查找对象内容中存在的属性。注意: .contents 通常会返回一个精确的项目列表,因此第一步是使用方括号表示法为项目建立索引。
example_content = example.contents[0]
example_content.attrs
现在,我可以看到 href 是一个属性,可以像字典项一样提取:
example_href = example_content['href']
print(example_href)
集成网站爬虫
所有这些探索都为我们提供了前进的道路。这是一个清理版本,以澄清上述逻辑。
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 well
for i in city_hrefs[:2]:
print(i)
输出是用于抓取爱达荷州 Family Dollar 商店的 URL 列表。
换句话说,我还没有得到地址信息!现在,您需要抓取每个城市的 URL 以获取此信息。因此,我们使用一个具有代表性的示例来重新启动该过程。
page2 = requests.get(city_hrefs[2]) # again establish a representative example
soup2 = BeautifulSoup(page2.text, 'html.parser')
家庭美元地图和代码
地址信息嵌套在 type="application/ld+json" 中。经过大量的地理位置爬取,我开始意识到这是一个存储地址信息的通用结构。幸运的是,soup.find_all() 支持类型搜索。
arco = soup2.find_all(type="application/ld+json")
print(arco[1])
地址信息在第二个列表成员中!我懂了!
使用 .contents 提取内容(从第二个列表项中)(这是过滤后合适的默认操作)。同样,由于输出是一个列表,我为列表项建立了一个索引:
arco_contents = arco[1].contents[0]
arco_contents
哦,看起来不错。此处提供的格式与 JSON 格式一致(并且,类型名称确实收录“json”)。JSON 对象的行为类似于带有嵌套字典的字典。一旦你熟悉了它,它实际上是一种很好的格式(当然,它比一长串正则表达式命令更容易编程)。虽然在结构上看起来像一个 JSON 对象,但它仍然是一个 bs4 对象,需要通过编程方式转换为 JSON 对象才能访问它:
arco_json = json.loads(arco_contents)
在内容中,有一个被调用的地址键,它要求地址信息在一个相对较小的嵌套字典中。可以这样检索:
arco_address = arco_json['address']
arco_address
好的,请注意。现在我可以遍历存储的爱达荷州 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 来组织我们的 网站 爬取结果
我们在字典中加载了大量数据,但是有一些额外的无用项使得重用数据变得比必要的复杂。为了执行最终的数据组织,我们需要将其转换为 Pandas 数据框,删除不必要的列@type 和 country,并检查前五行以确保一切正常。
locs_df = df.from_records(locs_dict)
locs_df.drop(['@type', 'addressCountry'], axis = 1, inplace = True)
locs_df.head(n = 5)
一定要保存结果!!
df.to_csv(locs_df, "family_dollar_ID_locations.csv", sep = ",", index = False)
我们做到了!爱达荷州的所有 Family Dollar 商店都有一个以逗号分隔的列表。多么激动人心。
Selenium 和数据抓取的一点解释
Selenium 是一种常用的工具,用于自动与网页交互。为了解释为什么有时需要使用它,让我们看一个使用 Walgreens 网站 的例子。“检查元素”提供浏览器显示内容的代码:
尽管“查看页面源代码”提供了有关请求将获得什么的代码:
如果这两个不一致,有插件可以修改源代码——因此,你应该在加载到浏览器后访问页面。requests 不能这样做,但 Selenium 可以。
Selenium 需要一个 Web 驱动程序来检索内容。事实上,它会打开一个网络浏览器并采集这个页面的内容。Selenium 功能强大——它可以通过多种方式与加载的内容交互(请阅读文档)。使用Selenium获取数据后,继续像之前一样使用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')
在 Family Dollar 的情况下,我不需要 Selenium,但是当呈现的内容与源代码不同时,我会继续使用 Selenium。
概括
总之,当使用网站爬行完成有意义的任务时:
如果您对答案感到好奇:
家庭美元位置图
在美国有很多 Family Dollar 商店。
完整的源代码是:
import requests
from bs4 import BeautifulSoup
import json
from pandas import DataFrame as df
page = requests.get("https://www.familydollar.com/locations/")
soup = BeautifulSoup(page.text, 'html.parser')
# find all state links
state_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 links
city_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 links
store_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 information
stores = []
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 parsing
stores_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)
① Python 电子书 2000 多本(主流经典书籍应有) ② Python 标准库资料(最全中文版) ③ 项目源代码(四十、五十个有趣经典的动手项目和源代码) ④ 简介转Python基础、爬虫、网页开发、大数据分析视频(适合小白学习)⑤Python学习路线图(告别无感学习)
Python超全数据库安装包学习路线项目源码免费分享