问题 使用Selenium从网页获取所有可见文本


我一直在谷歌搜索这一天没有找到答案,所以如果已经回答,请提前道歉。

我试图从大量不同的网站获取所有可见文本。原因是我想处理文本以最终对网站进行分类。

经过几天的研究,我认为Selenium是我最好的机会。我找到了一种方法来获取所有文本,使用Selenium,遗憾的是同一文本被多次抓取:

from selenium import webdriver
import codecs

filen = codecs.open('outoput.txt', encoding='utf-8', mode='w+')

driver = webdriver.Firefox()

driver.get("http://www.examplepage.com")

allelements = driver.find_elements_by_xpath("//*")

ferdigtxt = []

for i in allelements:

      if i.text in ferdigtxt:
          pass
  else:
         ferdigtxt.append(i.text)
         filen.writelines(i.text)

filen.close()

driver.quit()

if 里面的状况 for 循环是尝试消除多次获取相同文本的问题 - 然而,它不会仅在某些网页上按计划工作。 (它也使脚本变得更慢)

我猜测我的问题的原因是 - 当要求元素的内部文本时 - 我也得到嵌套在相关元素内的元素的内部文本。

有没有办法解决?有没有某种主要元素我抓住内在的文本?或者是一种完全不同的方式,可以让我达到目标?任何帮助都会非常感激,因为我对这一点没有任何想法。

编辑:我使用Selenium而不是Mechanize和Beautiful Soup的原因是因为我想要JavaScript招标文本


9767
2017-10-30 20:23


起源

lynx 和 w3c 可以通过CLI执行此操作。 - Blender
不应该像你的xpath那样 //body/text()? - Pankrat
你的代码看起来很明显: for i in allelements: if i.allelements in ferdigtxt: pass   如果 i 在... allelements, 然后 i.allelements 可能是一个bug。 - Dimitre Novatchev
另一个观察是你似乎比较了它们之间的整个文本节点,这种比较在几乎100%的情况下可能都是错误的。如果你真的想比较使用的单词,那么@unutbu的解决方案提供了这个。请编辑您的问题并明确定义问题。 - Dimitre Novatchev
@Blender:做 lynx 和 w3c 支持javascript? (我对此表示怀疑)。 - jfs


答案:


运用 LXML,您可以尝试这样的事情:

import contextlib
import selenium.webdriver as webdriver
import lxml.html as LH
import lxml.html.clean as clean

url="http://www.yahoo.com"
ignore_tags=('script','noscript','style')
with contextlib.closing(webdriver.Firefox()) as browser:
    browser.get(url) # Load page
    content=browser.page_source
    cleaner=clean.Cleaner()
    content=cleaner.clean_html(content)    
    with open('/tmp/source.html','w') as f:
       f.write(content.encode('utf-8'))
    doc=LH.fromstring(content)
    with open('/tmp/result.txt','w') as f:
        for elt in doc.iterdescendants():
            if elt.tag in ignore_tags: continue
            text=elt.text or ''
            tail=elt.tail or ''
            words=' '.join((text,tail)).strip()
            if words:
                words=words.encode('utf-8')
                f.write(words+'\n') 

这似乎得到了www.yahoo.com上的几乎所有文本,除了图像中的文本和一些随时间变化的文本(用javascript和刷新完成)。


9
2017-10-30 21:05



非常感谢你那个彻底的答案unutbu!你已经使用了很多我不熟悉的代码,所以它会在你的解决方案上阅读。我很抱歉我之前没有说明这一点 - 但我使用selenium的原因是为了确保我能获得javascript渲染文本 - 据我所知,你的解决方案不提供。话虽这么说,如果我找不到抓住html和javascript渲染文本的方法,我一定会试试你的解决方案。非常感谢你! - Rookie
上面发布的代码使用Selenium的webdriver,因此它将包含javascript呈现的文本。但是,如果您从浏览器访问yahoo.com,您会在页面顶部看到一个随时间变化或鼠标悬停在某些图像上的区域。我注意到上面的代码没有捕获该区域的所有可能文本。我不确定以编程方式修复此问题的最佳方法(多次重新加载页面?哎呀......)。除此之外,它应该适用于大多数网站。 - unutbu
哇,很高兴听到!非常感谢unutbu - 我一旦开始工作就会潜入你的代码:) - Rookie


这是一个变种 @ unutbu的回答

#!/usr/bin/env python
import sys
from contextlib import closing

import lxml.html as html # pip install 'lxml>=2.3.1'
from lxml.html.clean        import Cleaner
from selenium.webdriver     import Firefox         # pip install selenium
from werkzeug.contrib.cache import FileSystemCache # pip install werkzeug

cache = FileSystemCache('.cachedir', threshold=100000)

url = sys.argv[1] if len(sys.argv) > 1 else "https://stackoverflow.com/q/7947579"


# get page
page_source = cache.get(url)
if page_source is None:
    # use firefox to get page with javascript generated content
    with closing(Firefox()) as browser:
        browser.get(url)
        page_source = browser.page_source
    cache.set(url, page_source, timeout=60*60*24*7) # week in seconds


# extract text
root = html.document_fromstring(page_source)
# remove flash, images, <script>,<style>, etc
Cleaner(kill_tags=['noscript'], style=True)(root) # lxml >= 2.3.1
print root.text_content() # extract text

我把你的任务分成两部分:

  • 获取页面(包括由javascript生成的元素)
  • 提取文字

代码仅通过缓存连接。您可以在一个进程中获取页面并在另一个进程中提取文本,或者稍后使用不同的算法执行此操作。


5
2017-10-31 17:19