【问题标题】:Why can't extract all content in p node from the webpage with selenium?为什么不能从带有 selenium 的网页中提取 p 节点中的所有内容?
【发布时间】:2020-03-16 08:30:23
【问题描述】:

使用lxml提取p节点中的所有内容很简单,我从网页的p节点中提取所有内容,并使用以下代码将其写入文件/tmp/content1.txt

import urllib.request
import lxml.html
url = 'https://www.statnews.com/pharmalot/2020/03/13/gilead-coronavirus-covid19-clinical-trials/'
ob=urllib.request.urlopen(url).read()
root=lxml.html.document_fromstring(ob)
content=root.xpath("//p")
with open('/tmp/content1.txt','w') as fh:
    for etxt in content:
        fh.write(etxt.text_content() + '\n') 

现在用 selenium 做同样的工作,将解析后的内容写入content2.txt

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

chrome_options = Options()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument("--headless")
browser = webdriver.Chrome(options=chrome_options,executable_path='/usr/bin/chromedriver')

wait = WebDriverWait(browser, 30)
url = 'https://www.statnews.com/pharmalot/2020/03/13/gilead-coronavirus-covid19-clinical-trials/'
browser.get(url)
wait.until(lambda e: e.execute_script('return document.readyState') != "loading")
wait.until(EC.presence_of_all_elements_located([By.CSS_SELECTOR, "p"]))
content = browser.find_elements_by_xpath('//p')
with open('/tmp/content2.txt','w') as fh:
    for etxt in content:
        fh.write(etxt.text + '\n')

按照 Svetlana Levinsohn 的建议进行操作:尝试删除 chrome_options.add_argument("--headless")。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

chrome_options = Options()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
browser = webdriver.Chrome(options=chrome_options,executable_path='/usr/bin/chromedriver')

wait = WebDriverWait(browser, 30)
url = 'https://www.statnews.com/pharmalot/2020/03/13/gilead-coronavirus-covid19-clinical-trials/'
browser.get(url)
wait.until(lambda e: e.execute_script('return document.readyState') != "loading")
wait.until(EC.presence_of_all_elements_located([By.CSS_SELECTOR, "p"]))
content = browser.find_elements_by_xpath('//p')
with open('/tmp/content3.txt','w') as fh:
    for etxt in content:
        fh.write(etxt.text + '\n')

比较content1.txtcontent2.txtcontent3.txt

cd  /tmp
wc -c content1.txt
11442 content1.txt
wc -c content2.txt
838 content2.txt
wc -c /tmp/content3.txt
12105 /tmp/content3.txt

1.为什么用selenium删除chrome_options.add_argument("--headless")时会得到更多的行?为什么这个动作背后的原理是?
2.有没有办法用 selenium 和 lxml 获得相同的内容?

按照 supputuri 的建议,将最后一行更改为 fh.write(etxt.get_attribute("textContent") + '\n'),问题仍然存在。

wc -c content1.txt
12402 content1.txt
wc -c content2.txt
12410 content2.txt

让我们检查一下为什么 content2.txt 比 content1.txt 多 8 个字节。

diff content1.txt  content2.txt
1c1
< By Ed Silverman @Pharmalot 
---
> By Ed Silverman2 @Pharmalot3 
3,4c3,4
< As anticipation mounts over the prospects for an experimental Gilead Sciences (GILD) drug to combat the novel coronavirus, two Wall Street analysts suggested it remains uncertain whether the antiviral therapy will be successful after assessing a new paper that examined a dozen U.S. patients.
< The paper, published on a preprint server without peer review, described the epidemiology, clinical course, and viral characteristics of the first 12 U.S. patients with Covid-19, only three of whom were treated with remdesivir, which was developed to treat the Ebola virus but shelved after proving less effective than other drugs during testing. The analysis was conducted by the Centers for Disease Control and Prevention Covid-19 response team.
---
> As anticipation mounts over the prospects for an experimental Gilead Sciences (GILD4) drug to combat the novel coronavirus, two Wall Street analysts suggested it remains uncertain whether the antiviral therapy will be successful after assessing a new paper that examined a dozen U.S. patients.
> The paper5, published on a preprint server without peer review, described the epidemiology, clinical course, and viral characteristics of the first 12 U.S. patients with Covid-19, only three of whom were treated with remdesivir, which was developed to treat the Ebola virus but shelved after proving less effective than other drugs during testing. The analysis was conducted by the Centers for Disease Control and Prevention Covid-19 response team.
22,24c22,24
< Coronavirus
< drug development
< research
---
> Coronavirus10
> drug development11
> research12
26c26
<                                   Republish this article
---
>                                   Republish this article13
59c59
< ????
---
> 

content2.txt 中的字节,而不是 content1.txt 中的字节。

line1 2,3 线 3-4 4,5 线 22-24 10,11,12 第26行13

4 字节存储 2,3,4,5 8字节存储10,11,12,13

content1.txt 中的字节,而不是 content2.txt 中的字节。

line59  ????

对于??????,它需要4个字节f09f918d来存储。

4+8-4 = 8 = 12410-12402

注意:lxml 或 selenium 解析的内容是动态变化的,可能会得到 content1.txt 和 content2.txt 不同的字节。

是时候检查另一个重要问题了。
对于lxml解析的content1.txt的第一行。

By Ed Silverman @Pharmalot 

对于 selenium 解析的 content2.txt 中的第一行。

By Ed Silverman2 @Pharmalot3 

为什么selenium在这里添加23selenium添加一些原始网页中没有的数字,它们是什么意思?
而且我从未见过 javascript 代码来更改网页的 dom 树。
获取get_attribute("textContent")时如何防止selenium加号?

Vladimir M 通知所有号码都在原始站点中。 我做了验证。

import urllib.request
import lxml.html
url = 'https://www.statnews.com/pharmalot/2020/03/13/gilead-coronavirus-covid19-clinical-trials/'
ob=urllib.request.urlopen(url).read()
root=lxml.html.document_fromstring(ob)
content=root.xpath("//p[@class='author']")[0]
lxml.html.tostring(content)

我们得到html源代码:

 b'<p class="author">
     <em>By</em> 
     <a ...>Ed  Silverman</a> 
     <a ...>@Pharmalot</a> 
   </p>'

不要包含sup这样的标签Vladimir M显示:

<p class="author">
  <em>By</em> 
  <a ...>Ed Silverman</a>
  <sup class="footnote">3</sup> 
  <a ...>@Pharmalot</a>
  <sup class="footnote">4</sup> 
</p>

如果原始html源代码中包含sup标签,lxml中的text_content可以显示出来。

import lxml.html as lh
data = """<p class="author"><em>By</em> <a href="https://www.statnews.com/staff/ed-silverman/" \
class="author-name-link author-name author-main">Ed Silverman</a><sup class="footnote">3</sup> \
<a href="https://twitter.com/Pharmalot" class="author-social" target="_blank" rel="noopener">  \
@Pharmalot</a><sup class="footnote">4</sup> </p>"""
doc = lh.fromstring(data)
data = doc.xpath('//p')[0]
print(data.text_content())

输出如下:

By Ed Silverman3   @Pharmalot4 

我推断sup 这两个标签是由一些javascript 代码创建的。
为了提高我的javascript知识,最后一个问题与js有关:
如何知道哪个js文件创建了位于&lt;p class="author"&gt;节点的号码?
请回答并获得500分。

【问题讨论】:

  • 尝试删除chrome_options.add_argument("--headless"),可能会解决这个问题
  • 找到至少一个元素时返回所有元素的存在:selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/… 标准睡眠将是这里最简单的方法......(顺便说一句,你不需要第一次在那里等待,Selenium将在获取后等待就绪状态)
  • 如果您检查源代码,如果它实际上是一个链接(锚),则会在文本中添加数字。

标签: python-3.x selenium


【解决方案1】:

我一直在尝试解决您的问题和网站,试图弄清楚到底发生了什么。这是我发现的。 (我之前的回答可能是错误的,或者至少是不完整的)

首先,硒不会添加原始文件中没有的行。它们在原始站点中,只是 lxml 以不同的方式显示它们。我对lxml不太了解,所以不会进一步讨论。

其次,让我们弄清楚这些数字是什么。让我们来

By Ed Silverman3 @Pharmalot4 

它的代码是

<p class="author">
  <em>By</em> 
  <a ...>Ed Silverman</a>
  <sup class="footnote">3</sup> 
  <a ...>@Pharmalot</a>
  <sup class="footnote">4</sup> 
</p>

注意到数字了吗? (顺便说一句,自您的原始帖子以来,这些有所改变)

数字在那里。并且有一个逻辑可以显示这些数字。

接下来要检查的是:

https://www.w3schools.com/jsref/prop_node_innertext.asp

基本上,textContent 将返回元素内的所有文本。这就是您在代码中获得数字的原因。

innerText 将遵守元素的 CSS 可见性规则。所以,是的,使用 innerText 获得的文本确实少于使用 textContent 获得的文本。

但现在你必须决定,你究竟需要实现什么。如果需要,使用 innerText 应该是返回可见文本的正确方法。

content = driver.find_elements_by_xpath('//p')

with open('content0_innerText.txt','w') as fh:
    for etxt in content:
        fh.write(etxt.get_attribute('innerText') + '\n')

但是当我尝试它时,仍然有一些数字对于某些链接是可见的。也许它们应该在 CSS 中可见。在任何情况下,您都可以对元素的样式或页面进行一些修改以获得您想要的内容,例如,通过删除所有包含这些数字的元素:

content = driver.find_elements_by_xpath('//sup')
for etxt in content:
    driver.execute_script("return arguments[0].remove();", etxt)

import time
time.sleep(1)

content = driver.find_elements_by_xpath('//p')

with open('content0_innerText_remove.txt','w') as fh:
    for etxt in content:
        fh.write(etxt.get_attribute('innerText') + '\n')

您也可以尝试修改页面/元素的样式。但这可能比简单地删除这些工作更多。

希望这会有所帮助。

关于这个 'sup' 标签的添加位置

通常很难说哪个文件可以确定。通过在加载此站点后检查 chrome 中的网络选项卡,我怀疑该功能位于:

stat-theme.js 文件。 (https://www.statnews.com/wp-content/compiled/js/stat-theme.js?ver=7206f7890c08d8e03e22ec8af0b756cf39f84bae)

即函数processLinks。由于它是“编译”的,因此它的可读性不是很高。但似乎它所做的是遍历所有链接,进行一些模式匹配并在 href 元素之后插入 sup 元素。我不会在这里粘贴代码,因为我可能会违反许可证,但您应该能够在该文件中找到它。

它似乎是在初始化时调用的。从文件名来看,它是 wordpress 功能的一部分或其插件之一。

以前的版本:

我注意到,当相关文本实际上是锚点时,会出现额外的数字。经过一番搜索,我认为您遇到了类似的问题:

Difference between text and innerHTML using Selenium

也许您想按照建议使用 .get_attribute('innerText')。

【讨论】:

  • 有了.get_attribute('innerText'),我得到的内容比get_attribute("textContent")少。
  • @it_is_a_literature 很好。如链接问题中所述,它通常取决于如何呈现内部文本的 Web 驱动程序实现。你可以试试我在我的一个测试自动化项目中使用的东西。您可以尝试使用 execute_script 方法执行 javascript。这将允许您直接使用浏览器。缺点是你失去了在硒中寻找元素的所有好处。但是您至少可以尝试一下,如果这会返回带有 innerText 属性的正确文本。
  • 请给我看代码来获取不包含硒的数字的内容,我已经阅读了您提供的材料,仍然无法编写正确的代码。
  • @it_is_a_literature 我已经做了一些实验。更新了答案
  • 请看我更新的帖子中以Vladimir M结尾的内容,我推断sup元素是由一些javascript代码创建的,如何知道哪个js创建了数字?
【解决方案2】:

content1.txtcontent2.txt 之间的区别在于我们从源中获取文本的方式。

content1.txt 的情况下,你得到的是text_content(),但是当涉及到content2.txt 时,你得到的是text。并且 texttextContent 不同,因此您缺少 content2.txt 中的行数。无头解决方案中的解决方案是将最后一行更改为

 fh.write(etxt.get_attribute("textContent"))

当我使用普通浏览器运行时,顶部的标题有额外的 p 元素和 textContent TRY STAT PLUS,这在无头或 lxml 方法中不存在。由于这个新的p 元素,文件的大小略高于前两种方法。

浏览器截图: 无头屏幕截图:

除了 TRY STAT PLUSRead Now 其余所有文本内容在所有 3 种方法中都是相同的。

【讨论】:

  • 无头问题的原因是屏幕尺寸,尝试使用更高的无头画布,所有结果应该相同
  • 请看我更新的帖子,硒添加了一些原始网页中没有的数字,它们是什么意思?
  • 获取get_attribute("textContent")时如何防止selenium加数字?
  • 尝试etxt.get_attribute("innerText") 这应该会删除数字。
【解决方案3】:

您可能不想尝试将--headless 用于chrome_options。它确实加速了程序,但有时它根本不使用用户界面。据我所知,这可能是这里的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    相关资源
    最近更新 更多