Python - 提取带有ID的链接
我正在学习Python中的Beautiful Soup,想通过抓取数据来练习。我有一个HTML页面,格式是这样的……
span id listing-name-1
span class address
span preferredcontact="1"
a ID websiteLink1
span id listing-name-2
span class address
span preferredcontact="2"
a ID websiteLink2
span id listing-name-3
span class address
span preferredcontact="3"
a ID websiteLink3
这样一直到40个这样的条目。
我想获取这些类/ID里面的文本,并且希望它们的顺序和HTML页面上的顺序一致。
为了开始,我尝试了类似这样的代码来获取listing-name-1:
import urllib2
from BeautifulSoup import BeautifulSoup
page = urllib2.urlopen("http://www.yellowpages.com.au/search/listings?clue=architects&locationClue=New+South+Wales&x=45&y=12")
soup = BeautifulSoup(page)
soup.find(span,attrs={"id=listing-name-1"})
结果出现了远程主机强制关闭了一个现有的连接的错误。
我不知道该怎么解决这个问题。我需要帮助的有两个方面:
- 如何解决这个错误
- 我该如何从1到40遍历listing-name-1?我不想为所有40个Span ID都输入
soup.find(span,attrs={"id=listing-name-1"})
。
谢谢!
3 个回答
你第一个问题看起来跟Python没什么关系。试着打印一下 page.read()
,看看有没有输出。你也可以用浏览器打开这个页面,看看能不能加载出来。
至于你第二个问题,你可以给 findAll
传递一个正则表达式:
import re
import urllib2
from BeautifulSoup import BeautifulSoup
page = urllib2.urlopen("http://www.yellowpages.com.au/search/listings?clue=architects&locationClue=New+South+Wales&x=45&y=12")
soup = BeautifulSoup(page)
listing_names = re.compile('listing-name-[0-9]+')
listings = soup.findAll('span', id=listing_names)
print(listings)
上面的代码会打印出我电脑上的所有列表,所以你第一个问题肯定不是代码的问题。
你第二个问题的答案其实很简单:
import urllib2
from BeautifulSoup import BeautifulSoup
page = urllib2.urlopen("http://www.yellowpages.com.au/search/listings?clue=architects&locationClue=New+South+Wales&x=45&y=12")
soup = BeautifulSoup(page)
for num in range(1, 41):
soup.find("span", attrs={"id": "listing-name-"+str(num)})
使用 lxml.html
时,你可以直接用一个网址来调用 parse
,这样就不需要自己去调用 urllib
了。而且,和使用 find
或 findall
不同,你应该用 xpath
,这样可以充分利用 xpath 的强大功能。如果你用 find
来调用下面的表达式,会出现 invalid predicate
的错误。
#!/usr/bin/env python
import lxml.html
url = "http://www.yellowpages.com.au/search/listings?clue=architects&locationClue=New+South+Wales&x=45&y=12"
tree = lxml.html.parse(url)
listings = tree.xpath("//span[contains(@id,'listing-name-')]/text()")
print listings
这样会输出结果,并保持顺序:
['Cape Cod Australia Pty Ltd',
'BHI',
'Fibrent Pty Ltd Building & Engineering Assessments',
...
'Archicentre']
关于你在我回答下的评论中提到的问题,你想要查找的是 <div class="listingInfoContainer">...</div>
,这个部分包含了你想要的所有信息(比如名字、地址等)。然后你可以遍历那些符合条件的 div 元素,并使用 xpath 表达式来提取剩下的信息。需要注意的是,在这个例子中,我使用了 container.xpath('.//span')
,这会从当前节点(也就是容器 div)开始搜索。如果你省略了 .
,只用 //span
,那搜索就会从树的顶部开始,这样会得到所有匹配的元素列表,这不是你想要的结果。
#!/usr/bin/env python
import lxml.html
url = "http://www.yellowpages.com.au/search/listings?clue=architects&locationClue=New+South+Wales&x=45&y=12"
tree = lxml.html.parse(url)
container = tree.xpath("//div[@class='listingInfoContainer']")
listings = []
for c in container:
data = {}
data['name'] = c.xpath('.//span[contains(@id,"listing")]/text()')
data['address'] = c.xpath('.//span[@class="address"]/text()')
listings.append(data)
print listings
这会输出:
[{'name': ['Cape Cod Australia Pty Ltd'],
'address': ['4th Floor 410 Church St, North Parramatta NSW 2151']},
{'name': ['BHI'],
'address': ['Suite 5, 65 Doody St, Alexandria NSW 2015']},
{'name': ['Fibrent Pty Ltd Building & Engineering Assessments'],
'address': ["Suite 3B, Level 1, 72 O'Riordan St, Alexandria NSW 2015"]},
...
{'name': ['Archicentre'],
'address': ['\n Level 3, 60 Collins St\n ',
'\n Melbourne VIC 3000\n ']}]
结果是一个列表(同样保持你想要的顺序),里面是字典,每个字典都有 name
和 address
这两个键,每个键对应一个列表。这个最终的列表是通过 text()
返回的,它保留了原始 HTML 中的 \n
换行符,并把像 <br>
这样的标签转换成新的列表元素。比如,列表项 Archicentre 的原始 HTML 表示是:
<span class="address">
Level 3, 60 Collins St
<br/>
Melbourne VIC 3000
</span>