【问题标题】:Python, Matching uneven length scraped listsPython,匹配不均匀长度的刮掉列表
【发布时间】:2012-01-02 02:29:32
【问题描述】:

请准备好长时间阅读。我处于停顿状态,不知道在哪里寻找答案/还有什么可以尝试的。不用说我对编程有点陌生。过去几周一直在研究这个项目。

问题

我得到了这张表,25 行,2 列。每一行的结构如下:

需要的事件

<td align=center>19/11/11<br>12:01:21 AM</td>
<td align=center><font color=#006633><a href=profiles.php?XID=1><font color=#006633>player1</font></a> hospitalized <a href=profiles.php?XID=2><font color=#006633>player2</font></a></font></td>

不需要的事件 案例A

<td align="center">19/11/11<br />12:58:03 AM</td>
<td align="center"><font color="#AA0000">Someone hospitalized <a href=profiles.php?XID=1><font color="#AA0000">player1</font></a></font></td>

不需要的事件 案例B

<td align="center">19/11/11<br />12:58:03 AM</td>
<td align=center><font color=#006633><a href=profiles.php?XID=3><font color=#006633>player3</font></a> attacked <a href=profiles.php?XID=1><font color=#006633>player1</font></a> and lost </font></td>

我已经使用正则表达式来抓取所需的数据。我的问题是这两个列表没有匹配。日期和时间并不总是与确切的事件匹配。


第一次尝试解决问题

import mechanize  
import re

htmlA1 = br.response().read()

patAttackDate = re.compile('<td align=center>(\d+/\d+/\d+)<br>(\d+:\d+:\d+ \w+)')
patAttackName = re.compile('<font color=#006633>(\w+)</font></a> hospitalized ')
searchAttackDate = re.findall(patAttackDate, htmlA1)
searchAttackName = re.findall(patAttackName, htmlA1)

pairs = zip(searchAttackDate, searchAttackName)

for i in pairs:
print (i)

但这让我得到了wrong time - correct event 类型的列表。

例如:

(('19/11/11', '9:47:51 PM'), 'user1') <- mismatch 
(('19/11/11', '8:21:18 PM'), 'user1') <- mismatch
(('19/11/11', '7:33:00 PM'), 'user1') <- As a consequence of the below, the rest upwards are mismatched 
(('19/11/11', '7:32:38 PM'), 'user2') <- NOT a match, case B
(('19/11/11', '7:32:22 PM'), 'user2') <- match ok
(('19/11/11', '7:26:53 PM'), 'user2') <- match ok
(('19/11/11', '7:25:24 PM'), 'user3') <- match ok
(('19/11/11', '7:24:22 PM'), 'user3') <- match ok
(('19/11/11', '7:23:25 PM'), 'user3') <- match ok

第二次尝试解决问题

所以想从整个页面中删除newline并刮掉表格,但是:

import mechanize
import re
from BeautifulSoup import BeautifulSoup

htmlA1 = br.response().read()

stripped = htmlA1.replace(">\n<","><") #Removed all '\n' from code

soup = BeautifulSoup(stripped)

table = soup.find('table', width='90%')
table2 = table.findNext('table', width='90%')
table3 = table2.findNext('table', width='90%') #this is the table I need to work with

patAttackDate = re.compile('<td align="center">(\d+/\d+/\d+)<br />(\d+:\d+:\d+ \w+)')
searchAttackDate = re.findall(patAttackDate, table3)
print searchAttackDate

这给了我一个错误:

return _compile(pattern, flags).findall(string)
TypeError: expected string or buffer

我错过了什么?

奖金问题: 有没有办法解释 XID 是一个动态变量,但在使用 regex / beautifulsoup(或其他抓取方法)时绕过它?随着项目“增长”,我可能需要包含代码的 XID 部分,但不想与之匹配。 (不确定这是否清楚)

感谢您的宝贵时间


EDIT 1:添加列表示例
EDIT 2:使代码分离更加明显
EDIT 3:添加示例代码对于似乎不起作用的给定解决方案

Test = '''<table><tr><td>date</td></tr></table>'''
soupTest = BeautifulSoup(Test)
test2 = soupTest.find('table')
patTest = re.compile('<td>(.*)</td>')
searchTest = patTest.findall(test2.getText())
print test2 # gives: <table><tr><td>date</td></tr></table> 
print type(test2) # gives: <class 'BeautifulSoup.Tag'>
print searchTest #gives: []

编辑 4 - 解决方案

import re
import mechanize
from BeautifulSoup import BeautifulSoup

htmlA1 = br.response().read()
stripped = htmlA1.replace(">\n<","><") #stripped '\n' from html
soup = BeautifulSoup(stripped)

table = soup.find('table', width='90%')
table2 = table.findNext('table', width='90%')
table3 = table2.findNext('table', width='90%') #table I need to work with

print type(table3) # gives <class 'BeautifulSoup.Tag'>
strTable3 = str(table3) #convert table3 to string type so i can regex it

patFinal = re.compile(('(\d+/\d+/\d+)<br />(\d+:\d+:\d+ \w+)</td><td align="center">'
                      '<font color="#006633"><a href="profiles.php\?XID=(\d+)">'
                      '<font color="#006633">(\w+)</font></a> hospitalized <a'), re.IGNORECASE)
searchFinal = re.findall(patFinal, strTable3)

for i in searchFinal:
    print (i)

样本输出

('19/11/11', '1:08:07 AM', 'ID_user1', 'user1')
('19/11/11', '1:06:55 AM', 'ID_user1', 'user1')
('19/11/11', '1:05:46 AM', 'ID_user1', 'user1')
('19/11/11', '1:04:33 AM', 'ID_user1', 'user1')
('19/11/11', '1:03:32 AM', 'ID_user1', 'user1')
('19/11/11', '1:02:37 AM', 'ID_user1', 'user1')
('19/11/11', '1:00:43 AM', 'ID_user1', 'user1')
('19/11/11', '12:55:35 AM', 'ID_user2', 'user2')

EDIT 5 - 一个更简单的解决方案(第一次尝试 - 没有 Beautifulsoup)

import re

reAttack = (r'<td\s+align=center>(\d+/\d+/\d+)<br>(\d+:\d+:\d+\s+\w+)</td>\s*'
            '<td.*?' #accounts for the '\n'
            '<font\s+color=#006633>(\w+)</font></a>\s+hospitalized\s+')

for m in re.finditer(reAttack, htmlA1):
    print 'date: %s; time: %s; player: %s' % (m.group(1), m.group(2), m.group(3))

样本输出

date: 19/11/11; time: 1:08:07 AM; player: user1
date: 19/11/11; time: 1:06:55 AM; player: user1
date: 19/11/11; time: 1:05:46 AM; player: user1
date: 19/11/11; time: 1:04:33 AM; player: user1
date: 19/11/11; time: 1:03:32 AM; player: user1
date: 19/11/11; time: 1:02:37 AM; player: user1
date: 19/11/11; time: 1:00:43 AM; player: user1
date: 19/11/11; time: 12:55:35 AM; player: user2

【问题讨论】:

  • 难怪你有问题。您在 HTML 上使用正则表达式。
  • 您能否阐明所需案例的规则?因为我不确定使用直接正则表达式是在 python 中解决这个问题的最佳方法,而且我不明白你的确切日期问题
  • 我只是在等着看看有多少人忽略了您正在使用 BeautifulSoup 的事实并发布此链接。 stackoverflow.com/questions/1732348/…
  • @alonisser,我已经包含了一个输出示例

标签: python regex beautifulsoup


【解决方案1】:

根据你的描述,我还没有弄清楚你到底想做什么。但我现在可以告诉你一件事:对于正则表达式,Python 原始字符串是你的朋友。

尝试在 BeautifulSoup 程序中使用 r'pattern' 而不是仅使用 'pattern'

此外,当您使用正则表达式时,有时从简单的模式开始,验证它们是否有效,然后构建它们是很有价值的。您已经直接使用了复杂的模式,而且我确信它们不起作用,因为您没有使用原始字符串并且反斜杠也不正确。

【讨论】:

  • @chown 的回答也很有用(免费)!
  • r'pattern' 没有删减它。同样的错误。这些模式有效,但即使没有,它也会返回 [],而不是错误。
  • 我从未声称原始字符串可以解决您的所有问题。但我确实声称除非您使用原始字符串,否则反斜杠的内容不会按您期望的方式工作。您要么需要将每个反斜杠加倍,要么使用原始字符串。
【解决方案2】:

这对我有用:

reAttack = r'<td\s+align=center>(\d+/\d+/\d+)<br>(\d+:\d+:\d+\s+\w+)</td>\s*<td.*?<font\s+color=#006633>(\w+)</font></a>\s+hospitalized\s+'

for m in re.finditer(reAttack, htmlA1):
  print 'date: %s; time: %s; player: %s' % (m.group(1), m.group(2), m.group(3))

live demo

在一个正则表达式中执行所有操作会使正则表达式更加混乱,但这比单独匹配每个 TD 并尝试在之后同步它们要容易得多,就像你正在做的那样。正则表达式中间附近的 .*? 假设所有元素都由换行符分隔,就像在您的原始示例中一样。如果您不能这样假设,则应将 .*? 替换为 (?:(?!/?td&gt;).)* 以包含当前 TD 元素中的匹配项。

仅供参考,您的示例数据存在一些不一致之处。一些属性值被引用,而大多数没有被引用,并且您混合了&lt;br&gt;&lt;br /&gt; 标记。我为我的演示标准化了所有内容,但如果这代表您的真实数据,您将需要一个更复杂的正则表达式。或者您可以切换到纯 DOM 解决方案,这可能会更容易。 ;)

【讨论】:

  • 一个更简单的解决方案。谢谢。顺便说一句,引用的数据和不同的标签是第二次尝试使用 BeautifulSoup 的结果。
  • 也回答了奖金问题。我的印象是解析的数据必须按顺序排列(即我无法得到-日期、时间、用户-而不包括用户ID)
【解决方案3】:

.findNext() 方法将返回一个BeautifulSoup.Tag 对象,该对象不能传递给re.findall。因此,您需要使用.getText()(或类似的方法从Tag 对象中获取文本。或.contents 以获取该标记内的html)。另外,当使用re.compile时,返回的对象就是你需要调用findall的对象。

这个:

soup = BeautifulSoup(stripped)

table = soup.find('table', width='90%')
table2 = table.findNext('table', width='90%')
table3 = table2.findNext('table', width='90%') #this is the table I need to work with

patAttackDate = re.compile('<td align="center">(\d+/\d+/\d+)<br />(\d+:\d+:\d+ \w+)')
searchAttackDate = re.findall(patAttackDate, table3)

应该这样写(最后一行是唯一需要改变的):

soup = BeautifulSoup(stripped)

table = soup.find('table', width='90%')
table2 = table.findNext('table', width='90%')
table3 = table2.findNext('table', width='90%')

patAttackDate = re.compile('<td align="center">(\d+/\d+/\d+)<br />(\d+:\d+:\d+ \w+)')
searchAttackDate = patAttackDate.findall(table3.getText())

# or, to search the html inside table3 and not just the text
# searchAttackDate = patAttackDate.findall(str(table3.contents[0])) 

BeautifulSoup Documentation

来自re docs

re.compile(pattern, flags=0)

将正则表达式模式编译成正则表达式对象

这个:
result = re.match(pattern, string)

相当于:
prog = re.compile(pattern)
result = prog.match(string)

【讨论】:

  • searchAttackDate = re.findall(patAttackDate, htmlA1),工作正常...我已经发布了 2 种不同的解决问题的尝试,每一种都使用不同的变量。看起来我需要使代码分离更加明显。很抱歉混淆了
  • @k3rb3r05 哦,好的。但是,在问题的底部,错误:return _compile(pattern, flags).findall(string) TypeError: expected string or buffer 是因为您需要像这样调用findallpatAttackDate.findall(table3)
  • 同样的错误。我猜是 table3 (因为它是一种美丽的汤副产品)使它失控。如果我使用 htmlA1,它可以正常工作,但是我不会从 newline 中删除 HTML
  • @k3rb3r05 嗯,如果它仍然给出相同的TypeError 说它期待string or buffer,那么这意味着table3 不是一个字符串。可能是None?您可以在该调用上方添加print type(table3) 之类的内容以确保。
  • @k3rb3r05 对不起,我不小心给了你错误的方法。在您的编辑 3 中,将 patTest.findall(test2.getText()) 更改为 patTest.findall(str(test2.contents[0]))
【解决方案4】:

对于 beautifulsoup 解决方案,您可以使用它(无需检查正则表达式 - 我也确信 @steveha 关于 addin r'' 是正确的):

searchAttackDate = table3.findAll(patAttackDate)
for row in searchAttackDate:
   print row

【讨论】:

  • 我收到以下错误:searchAttackDate = table3.findall(patAttackDate) TypeError: 'NoneType' object is not callable
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-15
  • 2021-11-12
  • 1970-01-01
  • 2018-02-08
  • 2017-01-07
  • 2013-10-18
相关资源
最近更新 更多