抓取表格并从链接获取更多信息
我正在使用Python和BeautifulSoup来抓取一个表格……我对获取大部分需要的信息已经有了不错的掌握。以下是我想抓取的表格的简化版。
<tr> <td><a href="/wiki/Joseph_Carter_Abbott" title="Joseph Carter Abbott">Joseph Carter Abbott</a></td> <td>1868–1872</td> <td>North Carolina</td> <td><a href="/wiki/Republican_Party_(United_States)" title="Republican Party (United States)">Republican</a></td>
</tr>
<tr> <td><a href="/wiki/James_Abdnor" title="James Abdnor">James Abdnor</a></td> <td>1981–1987</td> <td>South Dakota</td> <td><a href="/wiki/Republican_Party_(United_States)" title="Republican Party (United States)">Republican</a></td> </tr> <tr> <td><a href="/wiki/Hazel_Abel" title="Hazel Abel">Hazel Abel</a></td> <td>1954</td> <td>Nebraska</td> <td><a href="/wiki/Republican_Party_(United_States)" title="Republican Party (United States)">Republican</a></td>
</tr>
http://en.wikipedia.org/wiki/List_of_former_United_States_senators
我想要的信息包括:姓名、描述、任职年份、州、政党。
描述是每个人页面上的第一段文字。我知道怎么单独获取这个信息,但我不知道怎么把它和姓名、任职年份、州、政党结合起来,因为我需要去不同的页面。
哦,对了,我还需要把这些信息写入一个CSV文件。
谢谢!
2 个回答
如果你在使用BeautifulSoup这个工具,你并不是像在浏览器里那样去点击链接跳转到另一个页面,而是直接用一个新的网址去请求那个页面,比如说
import urllib, csv
with open('out.csv','w') as f:
csv_file = csv.writer(f)
#loop through the rows of the table
for row in senator_rows:
name = get_name(row)
... #extract the other data from the <tr> elt
senator_page_url = get_url(row)
#get description from HTML text of senator's page
description = get_description(get_html(senator_page_url))
#write this row to the CSV file
csv_file.writerow([name, ..., description])
#quick way to get the HTML text as string for given url
def get_html(url):
return urllib.urlopen(url).read()
需要注意的是,在Python 3.x版本中,你要用urllib.request
来代替urllib
,而且你还得把read()
返回的bytes
数据进行解码。听起来你已经知道怎么填充我留在那里的其他get_*
函数了,希望这些信息对你有帮助!
我想进一步解释一下@anrosent的回答:在解析过程中发送请求是一个很好的、稳定的方法。不过,你用来获取描述的函数也必须正常工作,因为如果它返回一个NoneType
错误,整个过程就会乱套。
我在这方面的做法是这样的(注意我使用的是Requests库,而不是urllib或urllib2,因为我对这个更熟悉——你可以根据自己的喜好进行更改,逻辑是一样的):
from bs4 import BeautifulSoup as bsoup
import requests as rq
import csv
ofile = open("presidents.csv", "wb")
f = csv.writer(ofile)
f.writerow(["Name","Description","Years","State","Party"])
base_url = "http://en.wikipedia.org/wiki/List_of_former_United_States_senators"
r = rq.get(base_url)
soup = bsoup(r.content)
all_tables = soup.find_all("table", class_="wikitable")
def get_description(url):
r = rq.get(url)
soup = bsoup(r.content)
desc = soup.find_all("p")[0].get_text().strip().encode("utf-8")
return desc
complete_list = []
for table in all_tables:
trs = table.find_all("tr")[1:] # Ignore the header row.
for tr in trs:
tds = tr.find_all("td")
first = tds[0].find("a")
name = first.get_text().encode("utf-8")
desc = get_description("http://en.wikipedia.org%s" % first["href"])
years = tds[1].get_text().encode("utf-8")
state = tds[2].get_text().encode("utf-8")
party = tds[3].get_text().encode("utf-8")
f.writerow([name, desc, years, state, party])
ofile.close()
不过,这个尝试在David Barton
之后的那一行就结束了。如果你查看页面,可能是因为他占用了两行。这部分需要你自己来解决。错误追踪信息如下:
Traceback (most recent call last):
File "/home/nanashi/Documents/Python 2.7/Scrapers/presidents.py", line 25, in <module>
name = first.get_text().encode("utf-8")
AttributeError: 'NoneType' object has no attribute 'get_text'
另外,注意我的get_description
函数是在主要流程之前的。这显然是因为你必须先定义这个函数。最后,我的get_description
函数并不完美,因为如果某种情况下,单独页面中的第一个p
标签不是你想要的那个,它就会失败。
结果示例:
注意那些错误的行,比如Maryon Allen的描述。这部分也需要你来修正。
希望这些能给你指明方向。