使用Python For循环和BeautifulSoup将结果导出为CSV

2 投票
4 回答
877 浏览
提问于 2025-04-18 13:52

我从一个网站上抓取了地址数据,使用了一个循环来打印结果。我想把每次循环的结果输出到CSV文件的不同列中。但是当我尝试输出时,只能得到最后一次循环的结果,而且还包含了很多冗长的HTML代码。print street.text这行代码会打印出所有在标签中,且有itemprop = address的内容。

这是我目前的代码:

soup = BeautifulSoup(response, "lxml");

for address in soup.find_all('span', {'itemprop' : 'address'}):
    print address.text    

相关的HTML代码:

<span itemprop="address" itemscope="" itemtype="http://schema.org/PostalAddress" id="yui_3_15_0_1_1405702066072_1576"><a href="/homedetails/403-James-Toney-Dr-Elon-NC-27244/96315551_zpid/" class="hdp-link routable" title="403 James Toney Dr, Elon, NC Real Estate" id="yui_3_15_0_1_1405702066072_1575"><span itemprop="streetAddress" id="yui_3_15_0_1_1405702066072_1574">403 James Toney Dr</span>, <span itemprop="addressLocality">Elon</span>, <span itemprop="addressRegion" id="yui_3_15_0_1_1405702066072_1580">NC</span><span itemprop="postalCode" class="hide">27244</span></a></span>

这段代码会按顺序打印出所有数据。然而,我需要把每个地址放到CSV文件的一列中。这可能吗?我在想是不是需要在循环中把每个地址存到一个变量里,但我听说这样可能不是个好办法。我在几个网站上查找了解决方案,但感觉这应该很简单,我就是搞不定。

补充:我发现只用一个循环就能获取我需要的所有信息。希望这样能简化问题。

4 个回答

0

这是完整的解决方案。

这个方法是使用 csv.DictWriter 类,并且依赖于一个事实:你需要的所有字段在 csv 文件中都是用 span 标签和 itemprop 属性来表示的。

import csv
from bs4 import BeautifulSoup


data = """your html here"""
fieldnames = ['streetAddress', 'addressLocality', 'addressRegion', 'postalCode']

soup = BeautifulSoup(data, "lxml")

with open('output.csv', 'w') as f:
    writer = csv.DictWriter(f, fieldnames)
    for address in soup.find_all('span', itemprop='address'):
        writer.writerow({element['itemprop']: element.text
                         for element in address.find_all('span', itemprop=True)})

执行后,你提供的 HTML 源代码生成的 output.csv 文件内容是:

403 James Toney Dr,Elon,NC,27244
0

你可以这样做,使用一个平行列表,或者一个包含列表的字典(这取决于你),不过要注意,这样做只有在它们的大小相同的情况下才有效。你没有说明这一点,所以这个方法应该可以用。

from bs4 import BeautifulSoup
import csv
response = '<span itemprop="address" itemscope="" itemtype="http://schema.org/PostalAddress" id="yui_3_15_0_1_1405702066072_1576"><a href="/homedetails/403-James-Toney-Dr-Elon-NC-27244/96315551_zpid/" class="hdp-link routable" title="403 James Toney Dr, Elon, NC Real Estate" id="yui_3_15_0_1_1405702066072_1575"><span itemprop="streetAddress" id="yui_3_15_0_1_1405702066072_1574">403 James Toney Dr</span>, <span itemprop="addressLocality">Elon</span>, <span itemprop="addressRegion" id="yui_3_15_0_1_1405702066072_1580">NC</span><span itemprop="postalCode" class="hide">27244</span></a></span>'

soup = BeautifulSoup(response, "lxml")

addressList = soup.find_all('span', {'itemprop' : 'address'})

with open('addresses.csv', 'w') as f:
    writer = csv.writer(f)
    for address in addressList:
        writer.writerow([address.text])
1

一般来说,你需要提供一个HTML的例子,才能给出正确的答案。

在一种特殊情况下,如果每种元素的数量总是相同,并且它们的顺序也一致,你可以使用 zip 函数,比如:

streets = [street.text for street in soup.find_all(...)
towns = ....
states = ....
zips = ....

recs = zip(streets,towns,states,zips)

c = csv.writer(...)
for rec in recs:
    c.writerow(rec)
0

我用这个方法得到了我想要的结果:

addresses = []

soup = BeautifulSoup(response, "lxml" );

for address in soup.find_all ('span', {'itemprop' : 'address'}):
    addresses.append(address.text)

file = 'output.csv'
with open(file,'wb') as f:
    writer = csv.writer(f)
    writer.writerow(addresses)

谢谢大家的帮助!地址的长度并不全一样,但这似乎没有影响。所有的地址都完整地输出了。

撰写回答