我试图通过使用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列表中,那将不胜感激,并允许我通过示例进行学习。
最佳答案
使用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')
让我们用一些图像来说明下一步。
我们可能会感到困惑的是,有很多嵌套表,但是我们可以从考虑感兴趣的“顶层”表开始:
我们可以从中获取信息(将鼠标悬停在实际html中该图像的不同
trs
上,然后观察页面上突出显示的内容。可以在浏览器搜索框中输入p:nth-child(4) > table
来隔离此表):的确,实际的可见内容是嵌套的,但我们知道所有感兴趣的内容都在
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
,tr
的子font
节点的“顶级” Supplier
的同级tr:has(td:nth-of-type(1) font:contains(Supplier))
,然后删除包含tr
的“顶级” Aggregator
及其同级。由于html中有很长的trs
列表,因此您只想过滤掉感兴趣的部分之后的内容。看一下第一部分(我使用
select_one
而不是select
来演示匹配的第一个节点,而不是当我们添加general sibling combinator时匹配的多个同级兄弟-稍后会详细介绍) :soup.select_one('tr:has(td:nth-of-type(1) font:contains(Supplier))')
运行上面的给出:
将此与页面进行比较:
您将看到我们如何找到一种从顶层(尽管嵌套)包含
trs
标签开始选择顶层Supplier
的方法。但是,我们知道顶层是平坦的,因此我们需要删除(包括)Aggregator
标签中的所有内容。因此,我们将:not伪类添加到该通用同级组合器中。简而言之,我们说获得除兄弟姐妹之外的所有兄弟姐妹的所有trs
。现在,我们有了感兴趣的行的子集(绿色矩形)。我们对这些元素进行
:not
,并且每次找到与for loop
匹配的节点时,都会将'td:nth-of-type(2) font'
设置为找到的值,例如status
...这行:
if node is not None:
正在检查当前
Approved, Pending
是否包含“状态”子节点,例如tr
(我将其命名为Approved/Pending
作为输出),并相应地为此类别设置以后的行标识符。如果
status
没有此子节点,则我们知道它是容纳tr
的其他trs
之一,带有附加的信息列,例如:由于嵌套的级别,有一些空的
tds
包括我们不想要的。我们稍后使用大熊猫将其删除:df.drop([1,2,10], axis=1, inplace=True)
由于我们希望
tds
标签和所有status
都在一个列表tds
中,因此我使用row
扩展了第一个列表以包括第二个列表。我相信这比extend
快,但希望您对此感到满意。因此,我们可以从例如:
['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', '']
这些行将添加到名为
insert
的列表中。因此,您有一个列表列表(每行)。您可以将此列表传递给pandas.DataFrame以使用to_csv方法生成准备好进行csv导出的数据框。您使用
final
参数指定标题。生成行时:
tds = [td.text for td in tr.select('td')]
我们偶尔会发现其他空格和
columns
(换行符),例如['', '', '', '', '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')]
结果(也许不是最好的例子,但是是说明性的):
['', '', '', '', 'WFM Intermediary New England Energy, LLC', '', '125 Cambridgepark Dr, Cambridge, MA 02140', '', '07-10-08 11/28/2007 9/8/2011', '']
正则表达式:
最后(您到现在为止吗?),我们想为数据框添加一些标题。我们可以通过
\n
使用页面中的内容,并确保包含自定义的extend
标头。headers = ['Status']
headers.extend([th.text for th in soup.select('th[align]')])
Py:
from bs4 import BeautifulSoup as bs
import requests, re
import pandas as pd
r = requests.get('http://www.dpuc.state.ct.us/electric.nsf/$FormByElectricApplicantsView?OpenForm&Start=1&Count=1000&ExpandView')
soup = bs(r.content, 'lxml')
final = []
headers = ['Status']
headers.extend([th.text for th in soup.select('th[align]')])
for tr in 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)'):
node = tr.select_one('td:nth-of-type(2) font')
if node is not None:
status = node.text
else:
row = [status]
tds = [re.sub('\n+|\s+',' ',td.text) for td in tr.select('td')]
row.extend(tds)
final.append(row)
df = pd.DataFrame(final)
df.drop([1,2,10], axis=1, inplace=True)
df.columns = headers
df.to_csv(r'C:\Users\User\Desktop\Public Utilities.csv', sep=',', encoding='utf-8-sig',index = False )
输出样本:
补充阅读:
Css selectors