为什么 Selenium 仅获取页面上第一个 ToolTip 的文本?
作为使用 Python、Selenium 和 BeautifulSoup 构建的大型网络爬虫的一部分,我试图获取此页面上所有工具提示的文本: https://www.legis.state.pa.us/CFDocs/Legis/BS/bs_action.cfm?SessId=20190&Sponsors=S|44|0|Katie%20J.%20Muth
我当前的代码成功获取了所有链接并将鼠标悬停在每个链接上 - 当我运行它时,我会看到每个工具提示连续弹出。但是,它只输出第一个工具提示的文本。我不知道为什么!我以为我可能只需要在鼠标悬停之间等待更长的时间,但结果高达 20 秒,而且它并没有解决问题。
这是代码:
bill_links = soup.find_all('a', {'id': re.compile('Bill')})
summaries = []
bill_numbers = [link.text.strip() for link in bill_links]
for link in bill_links:
billid = link.get('id')
action = ActionChains(driver)
action.move_to_element(driver.find_element_by_id(billid)).perform()
time.sleep(5)
summary = driver.find_element_by_class_name("ToolTip-BillSummary-ShortTitle").text
print(summary)
summaries = summaries + [summary]
action.reset_actions()
同样,第一个 print(summary) 命令成功返回第一个工具提示的文本(“一项修正 1968 年 1 月 17 日法案的法案...”)——但每个后续 print(summary) 命令都只返回空白。
我对编程很陌生,所以如果有明显的答案,请原谅。
tl;dr:
不需要 Selenium。如果它确实是如图所示的工具提示(不是全文),则可以使用 bs4 并复制页面使用的 javascript 函数。函数调用的参数位于每个账单列表的 a 标签旁边的脚本标签中。我从适当的字符串中用正则表达式将这些内容传递给我们的用户定义函数(复制 jquery 函数)
您可以看到相关的调用
AddBillSummaryTooltip('#Bill_1',2019,0,'S','B','0012');
工具提示:
import requests
from bs4 import BeautifulSoup as bs
import re
def add_bill_summary_tooltip(s, session_year, session_ind, bill_body, bill_type, bill_no):
url = g_server_url + '/cfdocs/cfc/GenAsm.cfc?returnformat=plain'
data = { 'method' : 'GetBillSummaryTooltip',
'SessionYear' : session_year,
'SessionInd' : session_ind,
'BillBody' : bill_body,
'BillType' : bill_type,
'BillNo' : bill_no,
'IsAjaxRequest' : '1'
}
r = s.get(url, params = data)
soup = bs(r.content, 'lxml')
tooltip = soup.select_one('.ToolTip-BillSummary-ShortTitle')
if tooltip is not None:
tooltip = tooltip.text.strip()
return tooltip
g_server_url = "https://www.legis.state.pa.us"
#add_bill_summary_tooltip('#Bill_1',2019,0,'S','B','0012')
with requests.Session() as s:
r = s.get('https://www.legis.state.pa.us/CFDocs/Legis/BS/bs_action.cfm?SessId=20190&Sponsors=S|44|0|Katie%20J.%20Muth')
soup = bs(r.content, 'lxml')
tooltips = {item.select_one('a').text:item.select_one('script').text[:-1] for item in soup.select('.DataTable td:has(a)')}
p = re.compile(r"'(.*?)',(.*),(.*),'(.*)','(.*)','(.*)'")
for bill in tooltips:
arg1,arg2,arg3,arg4,arg5,arg6 = p.findall(tooltips[bill])[0]
tooltips[bill] = add_bill_summary_tooltip(s, arg2, arg3,arg4,arg5,arg6)
print(tooltips)
全文:
如果您想要全文,那么您可以从第一页抓取全文页面的链接,然后循环访问每个页面并抓取全文文本:
import requests
from bs4 import BeautifulSoup as bs
def add_bill_summary_full(s, url):
r = s.get(url)
soup = bs(r.content, 'lxml')
summary = soup.select_one('.BillInfo-Section-Data div')
if summary is not None:
summary = summary.text
return summary
g_server_url = "https://www.legis.state.pa.us"
with requests.Session() as s:
r = s.get('https://www.legis.state.pa.us/CFDocs/Legis/BS/bs_action.cfm?SessId=20190&Sponsors=S|44|0|Katie%20J.%20Muth')
soup = bs(r.content, 'lxml')
full_text = {item.text:g_server_url + item['href'] for item in soup.select('.DataTable a')}
for k,v in full_text.items():
full_text[k] = add_bill_summary_full(s, v)
print(full_text)
这是 jquery 使用的源代码 javascript 函数
function AddBillSummaryTooltip(element,SessionYear,SessionInd,BillBody,BillType,BillNo) {
jQuery(element).qtip({
content: {
text: function(event, api) {
jQuery.ajax({
url: g_ServerURL + '/cfdocs/cfc/GenAsm.cfc?returnformat=plain',
data: {
method: 'GetBillSummaryTooltip',
SessionYear: SessionYear,
SessionInd: SessionInd,
BillBody: BillBody,
BillType: BillType,
BillNo: BillNo,
IsAjaxRequest: 1
}
})
正则表达式:
请 此处 尝试。
说明:
如果您正在使用
selenium
,那么您就不必使用
BeautifulSoup
。要提取页面
https://www.legis.state.pa.us/CFDocs/Legis/BS/bs_action.cfm?SessId=20190&Sponsors=S|44|0|Katie%20J.%20Muth
上所有工具提示的文本,您可以使用以下解决方案:
-
代码块:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.action_chains import ActionChains chrome_options = webdriver.ChromeOptions() chrome_options.add_argument("start-maximized") chrome_options.add_argument('disable-infobars') driver = webdriver.Chrome(options=chrome_options, executable_path=r'C:\Utility\BrowserDrivers\chromedriver.exe') driver.get("https://www.legis.state.pa.us/CFDocs/Legis/BS/bs_action.cfm?SessId=20190&Sponsors=S|44|0|Katie%20J.%20Muth") for elem in WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.XPATH, "//table[@class='DataTable']/tbody//tr/td/a"))): senete_bill_shorten_number = elem.get_attribute("innerHTML").split()[1] ActionChains(driver).move_to_element(elem).perform() print(WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//div[@class='ToolTip-BillSummary']/div[@class='ToolTip-BillSummary-Title' and contains(., '" + senete_bill_shorten_number + "')]//following::div[2]"))).get_attribute("innerHTML"))
-
控制台输出:
一项修正 1968 年 1 月 17 日法案(P.L.11,第 5 号)的法案,称为 1968 年《最低工资法案》,进一步规定定义和最低工资;规定退休金;进一步规定执法和规章制度,规定人员…… 一项法案规定雇主为员工提供强制性全州带薪病假,并规定民事处罚和补救措施。 一项法案,修正宾夕法尼亚州综合法规第 42 条(司法和司法程序),在司法委员会和委员会中,规定采用行政缓刑违规指导方针;并在判刑时进一步规定…… 一项修正 1951 年 5 月 22 日法案(P.L.317,No.69)的法案,称为《专业护理法》,进一步规定了名称、定义、州护理委员会、营养师执照要求、未经授权的执业和行为…… 一项修正 1971 年 3 月 4 日法案(P.L.6,No.2)的法案,称为《1971 年税收改革法典》,规定了宾夕法尼亚州住房税收抵免。 一项修正 1959 年 12 月 3 日法案(P.L.1688,No.621)的法案,称为《住房融资机构法》,在宾夕法尼亚州住房可负担性和康复增强计划中,进一步规定了资金。 一项修正 1949 年 3 月 10 日法案(P.L.30,第 14 号),即 1949 年公立学校法典,关于特许学校,进一步规定了特许学校的资金。 一项修正 1967 年 6 月 13 日法案(P.L.31,第 21 号),即人类服务法典,关于部门权力和职责的监督,规定了儿童机构的铅检测;以及部门权力和职责的除虱…… 一项规定了水源保护的法案。 一项修正宾夕法尼亚州综合法规第 35 条(健康与安全)的法案,规定了紧急成瘾治疗;并对药物和酒精计划部施加权力和职责。 一项修正宾夕法尼亚州综合法规第 18 条(犯罪和违法行为)的法案,规定了动物的转让和销售。 一项修正宾夕法尼亚州综合法规第 42 条(司法和司法程序)的法案,特别是权利和豁免权,规定从机动车中救出未成年人的人员享有民事豁免权。 一项规定医疗保险覆盖范围保护、保险部门和保险专员职责、法规、执法和处罚的法案。 一项修正 1921 年 5 月 17 日法案(P.L.682,第 284 号),即 1921 年保险公司法,在意外保险中,为基本健康福利提供保障的法案。 一项修正 1955 年 10 月 27 日法案(P.L.744,第 222 号),即宾夕法尼亚州人际关系法案,进一步规定了定义和非法歧视行为。 一项修正宾夕法尼亚州综合法规第 18 条(犯罪和违法行为)和第 42 条(司法和司法程序)的法案,
问题可能出在这行代码中:
summary = driver.find_element_by_class_name("ToolTip-BillSummary-ShortTitle").text
您查找相应元素的条件仅受该元素的类名限制,这个单一条件可能会给您一个元素列表,但您实际上并没有指定要获取哪一个文本。
要解决此问题,请改用 xpath 表达式(您需要使用索引变量来定位元素):
summary = driver.find_element_by_xpath("//*[@id="qtip-" + <index> + "-content"]/div/div[3]").text