在完全平坦的 HTML 层次结构上使用 BeautifulSoup
Utilizing BeautifulSoup on a completely flat HTML hierarchy
所以我是一个网络抓取菜鸟,运行 变成了一些我以前从未见过的 HTML 格式。我需要的所有信息都在一个完全扁平的层次结构中。我需要抓住 Date/MovieName/Location/Amenities。
它的布局是这样的(就像这样):
<div class="caption">
<strong>July 1</strong>
<br>
<em>Top Gun</em>
<br>
"Location: Millennium Park"
<br>
"Amenities: Please be a volleyball tournament..."
<br>
<em>Captain Phillips</em>
<br>
"Location: Montgomery Ward Park"
<br>
<br>
<strong>July 2</strong>
<br>
<em>The Fantastic Mr. Fox </em>
我希望最终在字典或列表中具有格式,以便能够使用 csvwriter 或 Dictwriter 将其写为 CSV 文件;所以输出像
[7 月 1 日,壮志凌云,千禧公园,"Please be a volleyball tournament..."],
[7月1日,飞利浦船长,蒙哥马利沃德公园,]等
如您所见,令人讨厌的是,当两部电影在同一日期放映时,日期仅显示在第一部电影之前;然后列出的所有电影直到下一个 <strong>somedate<strong>
都属于该初始日期。
有什么建议吗?我怎样才能让多部电影都在上面的标签中指定的日期之内呢?可能想 find_next_siblings 包括检查标签是否是 <strong>
标签?
这是一个非常丑陋的解决方案,在您使用它之前应该使其更加健壮,但像这样的东西应该可以工作:
from bs4 import BeautifulSoup
import re
import csv
doc = """<div class="caption">
<strong>July 1</strong>
<br>
<em>Top Gun</em>
<br>
"Location: Millennium Park"
<br>
"Amenities: Please be a volleyball tournament..."
<br>
<em>Captain Phillips</em>
<br>
"Location: Montgomery Ward Park"
<br>
<br>
<strong>July 2</strong>
<br>
<em>The Fantastic Mr. Fox </em>
<br>
"Location: Somewhere"
<br>
"Amenities: Something something"
<br>"""
soup = BeautifulSoup(doc.replace("<br>", "<br/>"))
data = []
for date in soup.find_all("strong"):
sibling = date.next_sibling
while sibling and sibling.name != "strong":
if sibling.name == "em":
title = sibling
location = title.find_next("br").next
extra = location.find_next("br").next
row = []
row.append(date.text)
row.append(title.text)
row.append(re.findall('(?<=:)[^"]*', location)[0])
extra_val = re.findall('(?<=:)[^"]*', extra)
if len(extra_val):
row.append(extra_val[0])
data.append(row)
sibling = sibling.next_sibling
with open('foo.csv', 'wb') as csvfile:
writer = csv.writer(csvfile)
writer.writerows(data)
请注意 doc.replace("<br>", "<br/>")
,因为 BeautifulSoup 否则会将 <br>
标签解释为包含文档的所有其余部分。
进一步解释 <br>
与 <br/>
部分:
<p></p><em></em>
上面的HTML em
是p
的同胞。
<p><em></em></p>
在这个 HTML 中 em
是 p
的 child。现在让我们看看 BeautifulSoup 如何解析一些 HTML:
>>> from bs4 import BeautifulSoup
>>> BeautifulSoup('<br><p>Hello<br></p>', 'html.parser')
<br><p>Hello<br/></p></br>
>>> BeautifulSoup('<br><p>Hello<br></p>', 'html5lib')
<html><head></head><body><br/><p>Hello<br/></p></body></html>
html.parser
是 Pythons built-in HTML-parser,这是你默认得到的。如您所见,它添加了一个结束 </br>
标记并将一个 <br>
转换为 </br>
。简而言之,如果不关闭标签,它就不能很好地完成工作。这搞乱了哪些元素应该是兄弟姐妹。
另一方面,html5lib
试图匹配浏览器的行为,使用它代替 doc.replace("<br>", "<br/>")
也可以。然而,它要慢得多,而且它没有 Python 或 BeautifulSoup,所以它需要另一个 pip install html5lib
才能工作。
所以我是一个网络抓取菜鸟,运行 变成了一些我以前从未见过的 HTML 格式。我需要的所有信息都在一个完全扁平的层次结构中。我需要抓住 Date/MovieName/Location/Amenities。
它的布局是这样的(就像这样):
<div class="caption">
<strong>July 1</strong>
<br>
<em>Top Gun</em>
<br>
"Location: Millennium Park"
<br>
"Amenities: Please be a volleyball tournament..."
<br>
<em>Captain Phillips</em>
<br>
"Location: Montgomery Ward Park"
<br>
<br>
<strong>July 2</strong>
<br>
<em>The Fantastic Mr. Fox </em>
我希望最终在字典或列表中具有格式,以便能够使用 csvwriter 或 Dictwriter 将其写为 CSV 文件;所以输出像
[7 月 1 日,壮志凌云,千禧公园,"Please be a volleyball tournament..."], [7月1日,飞利浦船长,蒙哥马利沃德公园,]等
如您所见,令人讨厌的是,当两部电影在同一日期放映时,日期仅显示在第一部电影之前;然后列出的所有电影直到下一个 <strong>somedate<strong>
都属于该初始日期。
有什么建议吗?我怎样才能让多部电影都在上面的标签中指定的日期之内呢?可能想 find_next_siblings 包括检查标签是否是 <strong>
标签?
这是一个非常丑陋的解决方案,在您使用它之前应该使其更加健壮,但像这样的东西应该可以工作:
from bs4 import BeautifulSoup
import re
import csv
doc = """<div class="caption">
<strong>July 1</strong>
<br>
<em>Top Gun</em>
<br>
"Location: Millennium Park"
<br>
"Amenities: Please be a volleyball tournament..."
<br>
<em>Captain Phillips</em>
<br>
"Location: Montgomery Ward Park"
<br>
<br>
<strong>July 2</strong>
<br>
<em>The Fantastic Mr. Fox </em>
<br>
"Location: Somewhere"
<br>
"Amenities: Something something"
<br>"""
soup = BeautifulSoup(doc.replace("<br>", "<br/>"))
data = []
for date in soup.find_all("strong"):
sibling = date.next_sibling
while sibling and sibling.name != "strong":
if sibling.name == "em":
title = sibling
location = title.find_next("br").next
extra = location.find_next("br").next
row = []
row.append(date.text)
row.append(title.text)
row.append(re.findall('(?<=:)[^"]*', location)[0])
extra_val = re.findall('(?<=:)[^"]*', extra)
if len(extra_val):
row.append(extra_val[0])
data.append(row)
sibling = sibling.next_sibling
with open('foo.csv', 'wb') as csvfile:
writer = csv.writer(csvfile)
writer.writerows(data)
请注意 doc.replace("<br>", "<br/>")
,因为 BeautifulSoup 否则会将 <br>
标签解释为包含文档的所有其余部分。
进一步解释 <br>
与 <br/>
部分:
<p></p><em></em>
上面的HTML em
是p
的同胞。
<p><em></em></p>
在这个 HTML 中 em
是 p
的 child。现在让我们看看 BeautifulSoup 如何解析一些 HTML:
>>> from bs4 import BeautifulSoup
>>> BeautifulSoup('<br><p>Hello<br></p>', 'html.parser')
<br><p>Hello<br/></p></br>
>>> BeautifulSoup('<br><p>Hello<br></p>', 'html5lib')
<html><head></head><body><br/><p>Hello<br/></p></body></html>
html.parser
是 Pythons built-in HTML-parser,这是你默认得到的。如您所见,它添加了一个结束 </br>
标记并将一个 <br>
转换为 </br>
。简而言之,如果不关闭标签,它就不能很好地完成工作。这搞乱了哪些元素应该是兄弟姐妹。
html5lib
试图匹配浏览器的行为,使用它代替 doc.replace("<br>", "<br/>")
也可以。然而,它要慢得多,而且它没有 Python 或 BeautifulSoup,所以它需要另一个 pip install html5lib
才能工作。