BeautifulSoup如何从伪元素/类中获取href链接

3
我正在尝试解析https://www.tandfonline.com/toc/icbi20/current,获取所有文章的标题。HTML被分为多个卷和期。每个卷都有一个对应月份的期。因此,对于第36卷,将有12期。在当前卷(第37卷)中,有4个期刊,我想遍历每个期刊,并获取每篇文章的名称。
为了实现这一点并自动搜索,我需要获取每个期刊的href链接。最初,我选择了父div id:id = 'tocList'。
import requests
from bs4 import BeautifulSoup, SoupStrainer

chronobiology = requests.get("https://www.tandfonline.com/toc/icbi20/current")
chrono_coverpage = chronobiology.content

issues = SoupStrainer(id ='tocList')
issues_soup = BeautifulSoup(chrono_coverpage, 'html.parser', parse_only = issues)
for issue in issues_soup:
    print(issue)

这将返回一个bs4对象,但仅包含来自Volume div href 链接。更糟糕的是,这个 div 应该涵盖Volume div和Issue div
因此,我决定缩小搜索范围并使其更加具体化,并选择包含Issue href链接(class_='issues')的 div enter image description here 这次Jupiter会思考一下,但不会返回任何东西。什么也没有。空白。无。但是,如果我询问返回的“nothing”类型是什么,jupiter会告诉它是“String”???我真的不知道如何解释这个。
首先,我有一个问题,为什么Issue div元素不响应解析? 当我尝试运行print(BeautifulSoup(chrono_coverpage,'html.parser') .prettify())时,同样的情况发生了,Issue div不会出现(在html页面上的Inspect Element它立即出现在最终Volume span之下): enter image description here 因此,我怀疑它必须是面向javascript或其他东西,而不是面向HTML。或者 class ='open' 可能与此有关。
如何解析通过Javascript链接进行的链接?任何澄清都将不胜感激。
1个回答

2

好的,所以我已经“解决”了这个问题,但是我需要填补一些理论上的空白:

首先,这个片段是解决答案开端的关键:

enter image description here

可以看到,<div class = 'container'>紧随其后就是一个:: before伪元素,我感兴趣的链接包含在这个伪元素下方的div中。然后,这个最后的div:: after伪元素结束。
首先,我意识到我的问题在于需要选择一个伪元素。我发现使用BeutifulSoupsoup.select()非常困难,因为显然BeautifulSoup使用了Soup Sieve,它“旨在让用户使用CSS选择器来定位XML / HTML元素。 它实现了许多伪类[...]”。
段落的最后一部分声明:
Soup Sieve也不会匹配仅在实时浏览器环境中相关的 伪类 ,但如果已实现,则会优雅地处理它们;
这让我想到了一个问题,我不知道“仅在实时浏览器环境中相关的伪类”是什么意思。但随后我对自己说,“它也说如果被实现了,BS4应该能够解析它们”。既然我可以使用“检查”工具看到包含我感兴趣的href链接的div元素,那么我认为它一定被实现了。
那个短语的第一部分让我思考:“但是我需要实时浏览器才能使其工作吗?”
于是我想到了Selenium的Web驱动程序:
import requests
from bs4 import BeautifulSoup, SoupStrainer
from selenium import webdriver

driver = webdriver.Chrome()
url_chronobiology = driver.get("https://www.tandfonline.com/toc/icbi20/current")
chronobiology_content = driver.page_source
chronobiology_soup = BeautifulSoup(chronobiology_content)
chronobiology_soup.select('#tocList > div > div > div.yearContent > div.issues > div > div')

[Out]: []

很明显,这个结果让我感到难过,因为我以为自己已经理解了发生了什么。但是后来我想,如果我从之前打开的浏览器中“点击”其中一个问题,它就会起作用(说实话,我非常确定绝望促使了我那样想)。

好吧,意外的事情发生了:在点击“问题4”并重新运行脚本后,我得到了我想要的结果:

enter image description here

未解决的问题?

1 - 显然,这些伪元素只有在被点击时才“存在”,否则代码无法识别它们的存在。为什么?

2 - 为了使代码能够自动打开这些链接并解析我想要的信息(文章标题),必须运行哪些代码以进行初始单击并激活这些伪元素?

更新

使用Selenium的ActionChain回答问题2:

import requests
from bs4 import BeautifulSoup, SoupStrainer
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
url_chronobiology = driver.get("https://www.tandfonline.com/toc/icbi20/current")
chronobiology_content = driver.page_source
chronobiology_soup = BeautifulSoup(chronobiology_content)
action=ActionChains(driver)
action.move_to_element(driver.find_element_by_xpath('//*[@id="tocList"]/div/div/div[3]/div[2]/div')).perform()

chronobiology_soup.select('#tocList > div > div > div.yearContent > div.issues > div > div')

[Out]: 
[<div class="loi-issues-scroller">
 <a class="open" href="/toc/icbi20/37/4?nav=tocList">Issue<span>4</span></a>
 <a class="" href="/toc/icbi20/37/3?nav=tocList">Issue<span>3</span></a>
 <a class="" href="/toc/icbi20/37/2?nav=tocList">Issue<span>2</span></a>
 <a class="" href="/toc/icbi20/37/1?nav=tocList">Issue<span>1</span></a>
 </div>]

唯一的缺点是必须在页面上停留,以便 SeleniumActionChain.perform() 可以实际点击元素,但至少我已经自动化了这一步骤。
如果有人能回答问题1,那就太好了。

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