使用正则表达式解析HTML的Python

3
我正在尝试解析网站的HTML,查找班级的最大注册人数。我曾试图在HTML文件的每一行中检查子字符串,但那样会尝试解析错误的行。所以现在我正在使用正则表达式。我目前使用的正则表达式是\t\t\t\t\t\t\t<td class="odd">([0-9])|([0-9][0-9])|([0-9][0-9][0-9])<\/td>\r\n,但这个正则表达式匹配的是最大注册人数和课程编号。是否有其他方法可以提取我想要从网页中提取的内容?以下是HTML代码片段:
<tr>
    <td class="tableHeader">Section</td>
    <td class="odd">001</td>
</tr>

<tr>
    <td class="tableHeader">Credits</td>
    <td class="even" align="left">  4.00</td>
</tr>

<tr>
<td class="tableHeader">Title</td>
<td class="odd">Linear Algebra</td>
</tr>

<tr>
    <td class="tableHeader">Campus</td>
    <td class="even" align="left">University City</td>
</tr>

<tr>
    <td class="tableHeader">Instructor(s)</td>
    <td class="odd">Guang  Yang</td>
</tr>
<tr>
    <td class="tableHeader">Instruction Type</td>
    <td class="even">Lecture</td>
</tr>

<tr>
    <td class="tableHeader">Max Enroll</td>
    <td class="odd">30</td>
</tr>

3
请阅读:https://dev59.com/X3I-5IYBdhLWcg3wq6do#1732454 - jonrsharpe
2
不同意这个问题是重复的,它并没有询问是否可以使用正则表达式来完成,而是错误地尝试这样做。 - zmo
1
这不是重复内容。那个 OP 试图实际匹配标签名、类名等。我只是试图以这种方式提取内容,以便我不会得到节编号和最大注册人数。我只需要帮助获取仅最大注册人数。 - heinst
1
@LukasGraf,如果您能解释为什么不能做到而不是在评论区贬低别人,那将非常有帮助。 - heinst
2
这就是为什么我在我的全大写免责声明中提供链接的原因。我也可以使用<blink></blink>和toilet来编写它 - zmo
显示剩余12条评论
3个回答

5

不要使用正则表达式解析HTML。

用正确的工具做正确的工作。

让我们打个比方来解释为什么这是错误的:这就像试图让一个5岁的孩子理解哈姆雷特, 而他没有词汇和语法去理解莎士比亚,当他能够处理更抽象的概念时,他才会理解。

使用lxmlBeautifulSoup中的任一工具来完成此操作。

例如:获取所有偶数和奇数的列表:

>>> from lxml import etree
>>> tree = etree.HTML(your_html_text)
>>> odds = tree.xpath('//td[@class="odd"]/text()')
>>> evens = tree.xpath('//td[@class="even"]/text()')
>>> odds
['001', 'Linear Algebra', 'Guang  Yang', '30']
>>> evens
['  4.00', 'University City', 'Lecture']

编辑:

我只是想以一种不获取章节号和最大注册人数的方式提取内容。我只需要帮助获取最大注册人数。

好的,现在我明白你想要什么了,以下是使用lxml解决方案:

>>> for elt in tree.xpath('//tr'):
...     if elt.xpath('td[@class="tableHeader"]')[0].text == "Max Enroll":
...         elt.xpath('td[@class="odd"]|td[@class="even"]')[0].text
... 
'30'

这里只有最大注册人数。

使用BeautifulSoup会更容易一些:

>>> bs = BeautifulSoup(your_html_text)
>>> for t in bs.findAll('td', attrs={'class': 'tableHeader'}):
...   if t.text == "Max Enroll":
...     print t.findNext('td').text
'30'

1
soup.find('td', text="Max Enroll").find_next_sibling('td').text 会更容易些。 - alecxe
实际上,我在这里提供了更一般的方法,以便OP可以适应他的数据集。 - zmo

3
请使用专门解析html的工具,例如BeautifulSoup

Beautiful Soup是一个Python库,用于从HTML和XML文件中提取数据。它与您喜欢的解析器一起工作,为导航、搜索和修改解析树提供惯用方式。通常可以省去程序员数小时或数天的工作。

例如,这里是如何获取您想要的内容:
from bs4 import BeautifulSoup

data = """your html here"""

soup = BeautifulSoup(data)
print soup.find('td', text="Max Enroll").find_next_sibling('td').text

输出:

30

如果我选择这种方法,那么我将无法轻松地将此脚本提供给朋友使用,因为它将使用一个库,他们(很可能)最初没有安装在他们的计算机上,对吗? - heinst
@heinst 嗯,BeautifulSoup是一个可以轻松安装的第三方库。只需将脚本依赖项包含在requirements.txt文件中,并将其提供给您的朋友即可。 - alecxe

1

使用BeautifulSoup作为zmo的答案的替代方法:

from bs4 import BeautifulSoup

data = """
<snipped html>
"""

soup = BeautifulSoup(data)

for tableHeaders in soup.find_all('td', class_="tableHeader"):
    if tableHeaders.get_text() == "Max Enroll":
        print tableHeaders.find_next_siblings('td', class_="odd")[0].get_text()

输出:

30

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接