【问题标题】:How to extract tuples using findall?如何使用 findall 提取元组?
【发布时间】:2016-06-20 21:15:41
【问题描述】:

我正在尝试从 url 中提取元组,并且我已经成功地使用 re.search(pattern_str, text_str) 提取了 string texttuples .但是,当我尝试使用 re.findall(pattern_str, text_str) 提取元组列表时,我遇到了困难。

文字如下:

<li>
  <a href="11111">
    some text 111
    <span class="some-class">
      #11111
    </span>
  </a>
</li><li>
  <a href="22222">
    some text 222
    <span class="some-class">
      #22222
    </span>
  </a>
</li><li>
  <a href="33333">
    some text 333
    <span class="some-class">
      #33333
    </span>
  </a>
... # repeating
... 
... 

我正在使用以下模式和代码来提取元组:

text_above = "..." # this is the text above
pat_str = '<a href="(\d+)">\n(.+)\n<span class'
pat = re.compile(pat_str)
# following line is supposed to return the numbers from the 2nd line
# and the string from the 3rd line for each repeating sequence
list_of_tuples = re.findall(pat, text_above)

for t in list_of tuples:
    # supposed to print "11111 -> blah blah 111"
    print(t[0], '->', t[1])

也许我正在尝试一些奇怪且不可能的事情,也许使用原始字符串操作提取数据会更好......但万一有解决方案?

【问题讨论】:

  • 不要使用正则表达式解析 HTML。使用像美丽汤一样的解析器!

标签: python regex findall


【解决方案1】:

您的正则表达式没有考虑\n&lt;span 之间的空格(缩进)。 (而且你想要捕获的行开头的空白也不是,但这不是什么大问题。)要修复它,你可以添加一些\s*

pat_str = '<a href="(\d+)">\n\s*(.+)\n\s*<span class'

【讨论】:

  • 嗯...这是我的错。这似乎是一个合理的解决方案;所以,我试过pat_str = '&lt;a href="(\d+)"&gt;[\n\s]*(.+)[\n\s]*&lt;span class'。这次代码进入了 for 循环,但打印了整个文本;不在元组中。也就是说,列表只包含一个元素。代码的print 部分是否有问题。
  • @merkez3110 默认情况下,正则表达式的行为是贪婪的。按照您的操作方式,它将尝试找到最大的匹配,并且该匹配将从第一个 href 到最后一个 span。要么用*? 而不是* 让那些不贪心,要么只使用我的变体。
  • 现在我到了某个地方...如果您不介意,最后一个问题:在编译语句中使用re.DOTALL 是更好的解决方案还是应该留给\n 个字符?
  • @merkez3110 是的,您可能还可以将re.DOTALL'&lt;a href="(\d+)"&gt;(.+)&lt;span class' 之类的正则表达式一起使用,然后去除第二组周围的空格。这样 HTML 中的空白就没有那么重要了(至少在那个地方没有)。
【解决方案2】:

按照 cmets 中的建议,使用像 BeautifulSoup 这样的 html 解析器:

from bs4 import BeautifulSoup

h = """<li>
  <a href="11111">
    some text 111
    <span class="some-class">
      #11111
    </span>
  </a>
</li><li>
  <a href="22222">
    some text 222
    <span class="some-class">
      #22222
    </span>
  </a>
</li><li>
  <a href="33333">
    some text 333
    <span class="some-class">
      #33333
    </span>
  </a>"""

soup = BeautifulSoup(h)

你可以得到href和previous_sibling到span:

print([(a["href"].strip(), a.span.previous_sibling.strip()) for a in soup.find_all("a")])
[('11111', u'some text 111'), ('22222', u'some text 222'), ('33333', u'some text 333')]

或者是href和锚点的第一个内容:

print([(a["href"].strip(), a.contents[0].strip()) for a in soup.find_all("a")])

或使用.find(text=True) 仅获取标签文本,而不是从子级获取。

[(a["href"].strip(), a.find(text=True).strip()) for a in soup.find_all("a")]

另外,如果你只想要列表标签内的锚点,你可以专门解析这些:

[(a["href"].strip(), a.contents[0].strip()) for a in soup.select("li a")]

【讨论】:

  • 嗯,我的目的是学习并习惯re.search()re.findall()。我知道我可以使用 bs4 轻松解决问题,但我会在其他时间尝试一下(在学习 regex 之后)。
  • 这让我拔掉了头发,但最后我用 regex 做到了。然而,公平地说,当我尝试使用 bs4... 瞧!这是无与伦比的节省时间。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-14
  • 1970-01-01
  • 2013-07-30
  • 2021-11-27
  • 2017-08-12
  • 1970-01-01
  • 2015-10-08
相关资源
最近更新 更多