解析 HTML Python,BeautifulSoup

Posted

技术标签:

【中文标题】解析 HTML Python,BeautifulSoup【英文标题】:Parsing HTML Python, BeautifulSoup 【发布时间】:2014-03-06 21:36:51 【问题描述】:

我有几个包含以下类型信息的 html 文档:

<td class="principal-col">
<div class="pr-person">
<div class="name"><span id="pr_person-icon" class="bullet-male-left"></span><span class="person-link">Thomas A /Dumpling/</span></div>
<table class="events" border="0">
<tr>
<td class="factLabel">event1:&nbsp;</td>
<td>
4 February 1940          
<br/>
</td>
</tr> 
<tr>
<td class="factLabel">event2:&nbsp;</td>
<td>
9 October 2002   
<br/>Laplata, Md
</td>
</tr>

我正在尝试提取此人的姓名(此处:Thomas A Dumpling),以及 event1(此处:1940 年 2 月 4 日)和 event2 日期和地点(此处:2002 年 10 月 9 日,马里兰州拉普拉塔),从 html 文件中,并将内容放在 csv 文件“data.csv”的名为“Name”、“Event1”、“Event2”的列中。

如何从上面的 html 代码中提取名称到目前为止我还没有弄清楚。对于 event1 和 event2 日期信息,以下代码对于类似的 html 文件运行良好,但对于我在上面发布的 html 代码类型根本不起作用;也就是说,以下 Python 代码通过了,但它在 csv 文件的相应列中添加了“缺失”。

from bs4 import BeautifulSoup
import csv
import re
import glob
import os

f= csv.writer(open('data.csv', 'w'))
f.writerow(["Event1", "Event2"]) 

path = 'C:\\File-Path\\*'

for infile in glob.glob(os.path.join(path, "148929_S1N8-DQ7.htm")):
    soup = BeautifulSoup (open(infile))
    myDict = 
    for item in ["event1:", "event2:"]:
        try:
            myDict[item] = soup.find('td', text=re.compile(r'^%s$' % item)).findNext('td').text
            myDict[item] = myDict[item].strip()
            myDict[item] = myDict[item].lstrip()
            myDict[item] = myDict[item].rstrip()
            myDict[item] = myDict[item].encode('UTF-8')
        except Exception:
            myDict[item] = "missing"
            pass
    f.writerow([myDict["event1:"], myDict["event2:"]])

任何指针都非常感谢!

【问题讨论】:

【参考方案1】:

首先,我将您的示例数据转换为有效的 html 页面并进行漂亮的打印。这样可以更轻松地查看正在发生的事情:

<html><body><table><tr>
<td class="principal-col">
  <div class="pr-person">
    <div class="name">
      <span id="pr_person-icon" class="bullet-male-left"></span>
      <span class="person-link">Thomas A /Dumpling/</span>
    </div>
    <table class="events" border="0">
      <tr>
        <td class="factLabel">event1:&nbsp;</td>
        <td>4 February 1940<br/></td>
      </tr> 
      <tr>
        <td class="factLabel">event2:&nbsp;</td>
        <td>9 October 2002<br/>Laplata, Md</td>
      </tr>
    </table>
  </div>
</td>
</tr></table></body></html>

然后稍微切换一下你的程序:

from bs4 import BeautifulSoup
import csv
import glob
import os

DATA_PATH = "c:\\file_path\\"
FILESPEC  = "*.htm"
OUTFILE   = "data.csv"

def main():
    data = []
    for fname in glob.glob(os.path.join(DATA_PATH, FILESPEC)):
        with open(fname) as inf:
            pg = BeautifulSoup(inf.read())
            for person in pg.findAll('td', 'class':'principal-col'):
                data.append(get_data(person))
    data.sort()

    with open(os.path.join(DATA_PATH, OUTFILE), 'wb') as outf:
        outcsv = csv.writer(outf)
        outcsv.writerow(["Name", "Born", "Hired"])
        outcsv.writerows(data)

if __name__ == "__main__":
    main()

只留下实际的解析代码,

def get_string(node, default=''):
    if node:
        return ', '.join(node.stripped_strings)
    else:
        return default

def get_data(td_princ):
    name = get_string(td_princ.find('span', 'class':'person-link')).replace('/', '')

    birth = hired = '(missing)'
    for event in td_princ.find('table', 'class': 'events').findAll('tr'):
        cnt = [get_string(cell) for cell in event.findAll('td')]
        if len(cnt) == 2:
            if cnt[0] == "event1:":
                birth = cnt[1]
            elif cnt[0] == "event2:":
                hired = cnt[1]
    return (name, birth, hired)

当针对样本数据运行时,会生成一个看起来像这样的 csv 文件

Name,Born,Hired
Thomas A Dumpling,4 February 1940,"9 October 2002, Laplata, Md"

【讨论】:

谢谢休!如您所见,我对此真的很陌生...当我运行您的代码时,出现以下错误-您知道这可能是什么吗?非常感谢...“data.append(get_data(person)) NameError: global name 'get_data' is not defined” 我想通了——这对我来说是一个非常愚蠢的错误。解析代码实际上需要先走。现在像魅力一样工作 - 再次感谢 Hugh!【参考方案2】:

找到第一个标签的最简单方法是使用普通的findselect 也可以):

soup.find(class_='person-link')
Out[4]: <span class="person-link">Thomas A /Dumpling/</span>

soup.select('.person-link')
Out[5]: [<span class="person-link">Thomas A /Dumpling/</span>]

注意findclass_ 的特殊用法,因为class 是python 中的保留字。

'event1' 和 'event2' 使用select 更容易获取:

soup.select('td .factLabel ~ td')
Out[10]: 
[<td>
4 February 1940          
<br/>
</td>,
 <td>
9 October 2002   
<br/>Laplata, Md
</td>]

在上面的 css 选择器中,您要求 td 兄弟姐妹 td class="factLabel" 标记。

如果上述任何语法令人困惑,请前往BeautifulSoup docs。他们有很多很好的例子。

【讨论】:

以上是关于解析 HTML Python,BeautifulSoup的主要内容,如果未能解决你的问题,请参考以下文章

Python - 分配打印输出csv

用python的BeautifulSoup分析html

爬取中国大学排名

python 爬虫学习第三课

怎么用python解析html

python爬虫之html解析Beautifulsoup和Xpath