【问题标题】:Scraping text with Python Selenium: unable to locate element which really exist使用 Python Selenium 抓取文本:无法找到真正存在的元素
【发布时间】:2021-05-21 13:22:39
【问题描述】:

我试图从以下页面源中抓取文本:

我使用 selenium 和 python scrape“Diese Termine stehen zu ...”。

到目前为止我尝试了什么?

  1. 使用xpath查找元素并使用绝对位置:

availability = driver.find_elements_by_xpath("//*[@id='booking-content']/div[2]/div[4]/div/div[2]/div/div/div/div[1]/div/div/span")

  1. 使用类名:

elements = driver.find_elements_by_class_name("dl-text dl-text-body dl-text-regular dl-text-s dl-text-color-inherit")

  1. 使用 CSS 选择器:

使用以下关键字:.booking-message .dl-text

availability = driver.find_element_by_css_selector('.booking-message .dl-text')

以上所有方法均无效。通过第 3 步,我确信它应该可以工作,因为从屏幕截图中可以看出,我可以在 Chrome 中使用相同的关键字找到元素。但仍然没有运气。

错误信息是:

Traceback (most recent call last):
  File "/Users/GunardiLin/Desktop/Codes/Tracker.py", line 18, in <module>
    availability = driver.find_element_by_css_selector('.booking-message .dl-text')
  File "/Users/GunardiLin/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 598, in find_element_by_css_selector
    return self.find_element(by=By.CSS_SELECTOR, value=css_selector)
  File "/Users/GunardiLin/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 978, in find_element
    'value': value})['value']
  File "/Users/GunardiLin/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/Users/GunardiLin/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":".booking-message .dl-text"}
  (Session info: chrome=90.0.4430.212)

我知道另一个有同样问题的帖子: Python with selenium: unable to locate element which really exist

这就是我检查网站是否使用“iframe”的原因。 我通过搜索“iframe-tags”来检查它,就像在屏幕截图中一样。搜索结果为0,表示没有找到。

有人可以指点如何抓取文本吗? 我更喜欢使用 css 选择器(选项 3)并且不喜欢使用选项 1(xpath + 绝对位置)。但目前我会感谢任何解决方案。

提前谢谢你:-)

更新:

到目前为止我的代码:

import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import Select

url = r"https://www.doctolib.de/gemeinschaftspraxis/muenchen/fuchs-hierl?practitioner_id=any&speciality_id=5593&utm_campaign=website-button&utm_source=fuchs-hierl-website-button&utm_medium=referral&utm_content=custom&utm_term=fuchs-hierl"

chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver.Chrome(executable_path="/Applications/chromedriver", options=chrome_options)
driver.get(url)
print('*** Title:', driver.title)
# print(driver.page_source.encode("utf-8"))
dropdown_besuchgrund = driver.find_element_by_id("booking_motive")
select_besuchgrund = Select(dropdown_besuchgrund)
# print(dir(select_besuchgrund))
select_besuchgrund.select_by_visible_text("Erste Impfung Covid-19 (BioNTech-Pfizer)")
# availability = driver.find_elements_by_xpath("//*[@id='booking-content']/div[2]/div[4]/div/div[2]/div/div/div/div[1]/div/div/span")
#elements = driver.find_elements_by_class_name("dl-text dl-text-body dl-text-regular dl-text-s dl-text-color-inherit")
# availability = driver.find_element_by_css_selector('.booking-message .dl-text')
availability = driver.find_element_by_xpath(".//div[contains(@class,'booking-message')]/span")
print("***")
print(availability.text)
# for elem in elements:
#     print ("***", elem.text)
#     if elem.text == "Diese Termine stehen zu einem späteren Zeitpunkt wieder für eine Online-Buchung zur Verfügung. ":
#         print("*** Ausgebucht")
driver.quit()

@itronic1990 22.05.2021 07:45:我已经检查了您的建议:

driver.find_element_by_xpath(".//div[contains(@class,'booking-message')]/span").text

正如您在上面看到的,chrome 可以使用您的过滤器找到文本。但是如果我运行代码,它就找不到它。我的测试代码:

import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
url = r"https://www.doctolib.de/gemeinschaftspraxis/muenchen/fuchs-hierl"
chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver.Chrome(executable_path="/Applications/chromedriver", options=chrome_options)
driver.get(url)
element_text = driver.find_element_by_xpath(".//div[contains(@class,'booking-message')]/span").text
print(element_text)
driver.quit()

错误信息:

NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":".//div[contains(@class,'booking-message')]/span"}
  (Session info: headless chrome=90.0.4430.212)

我不明白为什么?谢谢你的建议。

【问题讨论】:

  • 也许您在应用driver.find_element_by_css_selector('.booking-message .dl-text') 之前错过了一些等待/延迟?
  • 你能分享那个网页的链接吗?
  • @gunardilin 你到底想得到什么?你的预期输出是什么?
  • @gunardilin 我打开了那个链接。我看不到任何与.booking-message .dl-text 定位器匹配的元素。我确实看到位于.booking-message 的元素,但里面什么都没有。
  • 通过等待/延迟我的意思是让一些预期的条件等待一些条件,例如让元素可见等。但我仍然看不到这个元素我不确定这是相关的。但是,该网站可能会针对不同位置提供不同的数据,因此它显示的不是您在那里看到的内容

标签: python selenium web-scraping


【解决方案1】:

您已经在 xpath 和类名中使用了 find_elements。对吗?

试试这个

driver.find_element_by_xpath(".//div[contains(@class,'booking-message')]/span").text

【讨论】:

  • 我已将所有代码/尝试包含在我的原始帖子中。我尝试了你的建议,但它仍然不起作用。这让我很困惑,为什么它不能工作......
  • 你能在开发者控制台中试试 xpath 看看它是否返回任何元素吗?
  • 嘿 itronic1990,我已尝试过您的建议。还是行不通。我已经更新了我原来的帖子来回答你的问题。基本上开发人员控制台可以使用过滤器找到它,但脚本没有...感谢您的进一步帮助:-)
【解决方案2】:

为什么要打扰 Selenium?直接从源中获取数据:

import requests

url = 'https://www.doctolib.de/availabilities.json'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'}
payload = {
'start_date': '2021-05-21',
'visit_motive_ids': '2820334',
'agenda_ids': '466608',
'insurance_sector': 'public',
'practice_ids': '25230',
'limit': '4'}

jsonData = requests.get(url, headers=headers, params=payload).json()

输出:

print(jsonData['message'])
Diese Termine stehen zu einem späteren Zeitpunkt wieder für eine Online-Buchung zur Verfügung. 

我不熟悉德语,否则我可以提高效率。基本上使用practice_id 输入它并从每个练习中获取数据。

import requests
from bs4 import BeautifulSoup
from datetime import datetime

# Get location practice_ids
url = 'https://www.doctolib.de/allgemeinmedizin/81667-muenchen'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'}

practice_ids_list = []
for page in range(1,100):
    payload = {'page':page}

    response = requests.get(url, headers=headers, params=payload)
    if response.status_code == 404:
        break
    
    else:
        print('Page: %s' %page)
        soup = BeautifulSoup(response.text, 'html.parser')
        divs = soup.find_all('div',{'class':'dl-search-result'})
        
        for div in divs:
            practice_id = div['id'].split('-')[-1]
            practice_ids_list.append(practice_id)

today = datetime.today().strftime('%Y-%m-%d')

url = 'https://www.doctolib.de/availabilities.json'
for practice_id in practice_ids_list:
    payload = {
    'start_date': today,
    'visit_motive_ids': '2820334',
    'agenda_ids': '466606',
    'insurance_sector': 'public',
    'practice_ids': '%s' %practice_id,
    'limit': '15'}
    
    jsonData = requests.get(url, headers=headers, params=payload).json()
    
   
    if jsonData['total'] == 0 and 'next_slot' not in jsonData.keys():
        #print('\t', jsonData['message'],'\n')
        print(practice_id)
    else:
        # Get Clinic Details
        clinic_url = 'https://www.doctolib.de/search_results/%s.json' %practice_id
        clinic_jsonData = requests.get(clinic_url, headers=headers).json()
        clinic_name = clinic_jsonData['search_result']['name_with_title']
        address = clinic_jsonData['search_result']['address']
        city = clinic_jsonData['search_result']['city']
        zipcode = clinic_jsonData['search_result']['zipcode']
        print('%s\n%s %s %s' %(clinic_name, address, city, zipcode))
        
        payload.update({'start_date':jsonData['next_slot']})
        jsonData = requests.get(url, headers=headers, params=payload).json()
        print('\n\t','*'*50,'\nThe follow dates are available:')
        for each_date in jsonData['availabilities']:
            if len(each_date['slots']) > 0:
                print('\t\t',each_date['date'])

【讨论】:

  • 哇,看起来很有希望。请原谅我,我是一个初学者程序员。使用正常请求的原因是什么?什么时候beautifulsoup 或selenium 更好?我没有想到直接使用请求。谢谢你的回答:-)
  • 你为什么要问我是否只对这个位置感兴趣?你的意思是我也可以为另一个位置运行相同的脚本吗?谢谢你的澄清
  • @gunardilin。是的,您可以在此处更改参数以查看不同的疫苗和不同的位置。只需找出这些 ID/代码,然后您就可以让脚本查看所有这些位置以检查日期。我会告诉你我的意思(我会调整上面的代码)。如果数据可以直接从 api 获取或返回为 json 格式,则使用简单请求(无需从 html 解析。如果您需要从 html 源代码中获取该数据,则使用请求获取html,然后 BS 从中解析数据。一切都失败了,使用 Selenium。
  • 转到开发工具 (shift-ctrl-i)。在 network -> XHR tabs 你可以看到它(你可能需要刷新页面)。至于参数,只是做了试验和错误(即,打开开发工具,点击网站上的更改内容,然后查看 XHR 并记录您点击的内容以及更改的内容)
  • 老实说,我从来没有做过在线教程或粗略的网络抓取。刚刚练习并查看了 SO 的不同方法。只是通过反复试验学到的。很抱歉没有任何建议。不过,我会环顾四周,看看如果我回去有没有什么,我希望我能做到。
猜你喜欢
  • 2014-08-13
  • 2021-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多