抓取jsp网页源代码(有人将robots.txt文件视为一组建议.py文件 )
优采云 发布时间: 2021-12-31 07:11抓取jsp网页源代码(有人将robots.txt文件视为一组建议.py文件
)
关于合法性,获得大量有价值的信息可能令人兴奋,但仅仅因为它是可能的并不意味着应该这样做。
幸运的是,有一些公共信息可以指导我们的道德和网络抓取工具。大多数网站都有一个与网站相关联的robots.txt文件,指明哪些爬行活动是允许的,哪些是不允许的。它主要用于与搜索引擎交互(网页抓取工具的终极形式)。但是,网站 上的大部分信息都被视为公开信息。因此,有些人将 robots.txt 文件视为一组建议,而不是具有法律约束力的文件。robots.txt 文件不涉及道德采集和数据使用等主题。
在开始抓取项目之前,先问自己以下问题:
当我抓取 网站 时,请确保您可以对所有这些问题回答“否”。
要详细了解这些法律问题,请参阅 Krotov 和 Silva 于 2018 年撰写的“Legal and Ethical Web Scraping”和 Sellars 的“Web Scraping and Computer Fraud and Abuse Act 20 Years of Web Scraping and Computer Fraud and Abuse Act”。
现在开始爬取网站
经过上面的评估,我想出了一个项目。我的目标是爬取爱达荷州所有 Family Dollar 商店的地址。这些店在农村很大,所以我想知道有多少这样的店。
起点是Family Dollar的位置页面
爱达荷州家庭美元位置页面
首先,让我们在 Python 虚拟环境中加载先决条件。此处的代码将添加到 Python 文件(如果需要名称,则为 scraper.py)或在 JupyterLab 的单元格中运行。
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
接下来,我们从目标 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_ 代替。soup.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 exampleexample_content = example.contentsprint(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 wellfor i in city_hrefs[:2]: print(i)
输出是用于抓取爱达荷州 Family Dollar 商店的 URL 列表。
也就是说,我还没有得到地址信息!现在,您需要抓取每个城市的 URL 以获取此信息。因此,我们使用一个具有代表性的示例来重新启动该过程。
page2 = requests.get(city_hrefs[2]) # again establish a representative examplesoup2 = 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)
type(arco_json)print(arco_json)
在内容中,有一个被调用的地址键,它要求地址信息在一个相对较小的嵌套字典中。可以这样检索:
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 网站 的例子。“检查元素”提供浏览器显示内容的代码:
沃尔格林位置页面和代码
尽管“查看页面源代码”提供了有关请求将获得什么的代码:
沃尔格林源代码
如果这两个不一致,有插件可以修改源代码——因此,你应该将它加载到浏览器中后访问页面。请求无法做到这一点,但 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 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 inpidual 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)