从<a>标记内的网页中提取公司名称

时间:2019-09-11 19:35:15

标签: python html web-scraping beautifulsoup python-requests

我正在尝试使用Python 3.7和BeautifulSoup简化公司的数据收集工作,以提取公司名称(如果该公司已获批准或其他),以及他们是否正在从此CT许可证列表中向住宅和/或企业进行营销:

http://www.dpuc.state.ct.us/electric.nsf/$FormByElectricApplicantsView?OpenForm&Start=1&Count=1000&ExpandView

如果我能获得如何完成代码的帮助,以便至少在Python中创建一个附加公司名称的列表,那将是一个很大的帮助。

我正在学习,能够连接到该站点并获取源代码。

到目前为止,我知道代码的这一部分可以正常工作,但不确定从何处去

import requests
from bs4 import BeautifulSoup
result = requests.get("http://www.dpuc.state.ct.us/electric.nsf/$FormByElectricApplicantsView?OpenForm&Start=1&Count=1000&ExpandView")
src = result.content
soup = BeautifulSoup(src,'lxml')

我可以在源代码中看到公司名称,但不确定将其提取的最佳方法以及将其提取到列表中的最佳方法:

<a href="/electric.nsf/c39dc573ab1299538525743b004d4df6/719f088ca20a6a1f85257dfd00480e13?OpenDocument">3Degrees Group, Inc.</a>

我很希望能够将所有这些内容导入到CSV中。在某个时候提交文件,其中包含公司,许可证的状态以及他们的市场对象,但是如果有人可以帮助我完成代码以将公司放在Python列表中,那将不胜感激,并允许我通过示例进行学习。

2 个答案:

答案 0 :(得分:1)

使用bs4 4.7.1+,您可以使用:contains:has仅过滤与Supplier相关的部分。您可以进一步将df子集化为感兴趣的列。


tl; dr;

发出请求并将响应读入汤对象:

r = requests.get('http://www.dpuc.state.ct.us/electric.nsf/$FormByElectricApplicantsView?OpenForm&Start=1&Count=1000&ExpandView')
soup = bs(r.content, 'lxml')

让我们用一些图像来说明下一步...

我们拥有的可能是令人困惑的是很多嵌套表,但是我们可以从考虑感兴趣的“顶级”表开始:

enter image description here

我们可以从中获取(将鼠标悬停在实际html中该图像的不同trs上,然后观察页面上突出显示的内容。您可以在浏览器搜索框中输入p:nth-child(4) > table隔离此表):

enter image description here

是的,实际可见的内容是嵌套的,但我们知道所有感兴趣的内容都在trs之内,在给定级别上是一系列同级trs

此位

soup.select('tr:has(td:nth-of-type(1) font:contains(Supplier)) ~ tr:not(:has(td:nth-of-type(1) font:contains(Aggregator)), :has(td:nth-of-type(1) font:contains(Aggregator)) ~ tr)')

收集个子trs节点包含 {{1}的“顶级” tr的同级font },Supplier,然后删除“最高”级别tr:has(td:nth-of-type(1) font:contains(Supplier))包含的tr及其兄弟姐妹。由于html中包含一长串Aggregator,因此您只想过滤掉感兴趣的部分之后的内容。

看第一部分(我使用trs而不是select_one来演示匹配的第一个节点,而不是当我们添加{{ 3}}-稍后再介绍):

select

运行上面的命令可获得:

general sibling combinator

将此与页面比较:

enter image description here

您会看到我们如何找到一种从顶部(尽管嵌套)包含soup.select_one('tr:has(td:nth-of-type(1) font:contains(Supplier))') 标签开始选择顶层trs的方法。但是,我们知道顶层是平坦的,因此我们需要删除Supplier标签中的所有内容(包括其中)。因此,我们向该通用同级组合器添加了enter image description here伪类。简而言之,我们说获得Aggregator

内所有兄弟姐妹trs

:not

现在,我们有了感兴趣的行的子集(绿色矩形)。我们:not遍历这些,每次找到与for loop匹配的节点,我们都将'td:nth-of-type(2) font'设置为找到的值,例如status .....

enter image description here

此行:

Approved, Pending

正在检查当前if node is not None: 是否包含'状态'子节点,例如tr(我将其命名为Approved/Pending进行输出),并相应地为此类别设置以后的行标识符。

如果status没有此子节点,那么我们知道它是容纳tr的其他trs之一,并带有附加的输出信息列,例如:

enter image description here

由于嵌套级别的原因,有一些空的tds包括我们不想要的。这些我们稍后会使用熊猫删除:

tds

由于我们希望在一个列表df.drop([1,2,10], axis=1, inplace=True) 中使用status标签和所有tds,所以我使用row扩展了第一个列表以包括第二个列表。我相信这比extend更快,但希望您对此感到满意。

因此,我们可以从例如:

insert

['Approved'] 

['', '', 'Yes', 'Yes', '3Degrees Group, Inc.', 'http://www.3degreesinc.com', '235 Montgomery Street, Suite 320 San Francisco, CA 94104', '(866) 476-9378', '11-11-07 12/14/2011', ''] 

这些行将添加到名为['Approved', '', '', 'Yes', 'Yes', '3Degrees Group, Inc.', 'http://www.3degreesinc.com', '235 Montgomery Street, Suite 320 San Francisco, CA 94104', '(866) 476-9378', '11-11-07 12/14/2011', ''] 的列表中。因此,您有一个列表列表(每行)。

您可以将此列表传递到enter image description here来生成一个准备好使用pandas.DataFrame方法进行csv导出的数据框。您可以使用final参数来指定标题。

生成行时:

columns

我们偶尔发现了额外的空白和tds = [td.text for td in tr.select('td')] (换行符),例如

\n

我实现了一个简单的正则表达式来删除它:

['', '', '', '', 'WFM Intermediary New England Energy, LLC', '', '125 Cambridgepark Dr, Cambridge, MA 02140', '', '07-10-08  \n11/28/2007\n9/8/2011', ''] 

产生的结果(也许不是最好的例子,但是是说明性的):

tds = [re.sub('\n+|\s+',' ',td.text) for td in tr.select('td')]

正则表达式:

to_csv

最后(您到现在为止吗?),我们想为数据框添加一些标题。我们可以通过['', '', '', '', 'WFM Intermediary New England Energy, LLC', '', '125 Cambridgepark Dr, Cambridge, MA 02140', '', '07-10-08 11/28/2007 9/8/2011', ''] 使用该页面中的内容,并确保我们包含自定义的extend标头。

status

enter image description here


Py:

headers = ['Status']
headers.extend([th.text for th in soup.select('th[align]')])

输出示例:

enter image description here


其他阅读内容:

  1. enter image description here

答案 1 :(得分:0)

company_list = [];
for company in soup.find_all('a'):
    company_list.append(company.string);

您可以在BeautifulSoup文档中找到有关此内容的更多详细信息。您可能需要修改此代码,以通过循环中的条件过滤掉特定的标签,但我认为这应该非常简单。

更具体地说,对于您的特定示例,您可以通过正则表达式或任何您喜欢的选项检查company.get('href')与“ .nsf”的匹配项。