Selenium 是一个弱小的网络数据采集工具,其最后是为网站自动化测试而开发的。近几年,它还被宽泛用于获取准确的网站快照,因为它们能够间接运行在浏览器上。Selenium 能够让浏览器主动加载页面,获取须要的数据,甚至页面截屏,或者判断网站上某些动作是否产生。
Selenium 本人不带浏览器,它须要与第三方浏览器联合在一起应用。例如,如果你在 Firefox 上运行 Selenium,能够间接看到一个 Firefox 窗口被关上,进入网站,而后执行你在代码中设置的动作。尽管这样能够看得更分明,然而我更喜爱让程序在后盾运行,所以我用一个叫 PhantonJS 的工具代替实在的浏览器。
PhantomJS 是一个“无头”(headles)浏览器。它会把网站加载到内存并执行页面上的 JavaScript,然而它不会向用户展现网页的图形界面。把 Selenium 和 PhantomJS 联合在一起,就能够运行一个十分弱小的网络爬虫了,能够解决 cookie、JavaScript、header、以及任何你须要做的事件。
你能够从 PyPI 网站 下载 Selenium 库,也能够用第三方管理器(像 pip)用命令行装置。
PhantomJS 也能够从它的官方网站下载。因为 PhantomJS 是一个功能完善(尽管无头)的浏览器,并非一个 Python 库,所以它不须要像 Python 的其余库一样装置,也不能用 pip 装置。
尽管有很多页面都用 Ajax 加载数据(尤其是 Google),咱们找到了一个页面全副是由 JavaScript 生成的,同时也是一个 PWA 利用,利用名称是 SMS America,来测试咱们的爬虫。这个页面上一些电话号码,与短信内容,所有内容都是 JavaScript 生成的。如果咱们传统的办法采集这个页面,只能获取加载前的页面,而咱们真正须要的信息(Ajax 执行之后的页面)却抓不到。
Selenium 库是一个在 WebDriver 上调用的 API。WebDriver 有点儿像能够加载网站的浏览器,然而它也能够像 BeautifulSoup 对象一样用来查找页面元素,与页面上的元素进行交互(发送文本、点击等),以及执行其余动作来运行网络爬虫。
上面的代码能够获取 Ajax“墙”前面的内容:
import os
from dotenv import load_dotenv
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.common.exceptions import WebDriverException
from config import logger_config
class TestWebDriver(object):
def __init__(self):
load_dotenv()
logger_name = 'Web Scraping to SMS America'
self._logger_write_file = logger_config.LoggingConfig().init_logging(logger_name)
self._chrome_path_file = os.getenv('CHROME_PATH')
def get_asn_content(self, link):
driver = webdriver.Chrome(executable_path=os.getenv('CHROME_PATH'))
driver.get(link)
try:
WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.ID, "prefixes")))
get_content = driver.find_element(By.TAG_NAME, "app-home").text
except (WebDriverException, UnboundLocalError) as e:
self._logger_write_file.error(f'解决 app-home 的时候呈现谬误,具体谬误内容:{e}, 地址:{link}')
return False
finally:
driver.quit()
return get_content
def main(self):
link = "https://america.storytrain.info/home"
self.get_asn_content(link)
if __name__ == '__main__':
TestWebDriver().main()
下面代码应用 webDriver 和 Chrome 浏览器的形式,首先 Chrome 库创立了一个新的 Selenium WebDriver,首先用 WebDriver 加载页面,而后暂停执行 10 秒钟,再查看页面获取(心愿曾经加载实现的)内容。
根据你的 Chrome 装置地位,在创立新的 Chrome WebDriver 的时候,你须要在 Selenium 的 WebDriver 接入点指明 Chrome 可执行文件的门路:
driver = webdriver.Chrome(executable_path=os.getenv('CHROME_PATH'))
因为 Selenium 降级到 v4.0.0 以上应用下面代码会呈现正告,所以咱们从新批改代码为如下:
import os
from dotenv import load_dotenv
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.common.exceptions import WebDriverException
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from config import logger_config
class TestWebDriver(object):
def __init__(self):
load_dotenv()
logger_name = 'Web Scraping to SMS America'
self._logger_write_file = logger_config.LoggingConfig().init_logging(logger_name)
self._chrome_path_file = os.getenv('CHROME_PATH')
def get_asn_content(self, link):
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get(link)
try:
WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CLASS_NAME, "home-page-card-container")))
get_content = driver.find_element(By.CLASS_NAME, "title-phone-number").text
except (WebDriverException, UnboundLocalError) as e:
self._logger_write_file.error(f'解决 app-home 的时候呈现谬误,具体谬误内容:{e}, 地址:{link}')
return False
finally:
driver.quit()
print(get_content)
return get_content
def main(self):
link = "https://america.storytrain.info/home"
self.get_asn_content(link)
if __name__ == '__main__':
TestWebDriver().main()
如果程序都配置正确,下面的程序会在几分钟之后显示上面的后果:
7743186342
尽管这个办法见效了,然而效率还不够高,在解决规模较大的网站时还是可能会出问题。页面的加载工夫是不确定的,具体依赖于服务器某一毫秒的负载状况,以及一直变动的网速。尽管这个页面加载可能只须要花两秒多的工夫,然而咱们设置了十秒的等待时间以确保页面齐全加载胜利。一种更加无效的办法是让 Selenium 一直地查看某个元素是否存在,以此确定页面是否曾经齐全加载,如果页面加载胜利就执行前面的程序。
上面的程序用 class 是 home-page-card-container 的页面内容查看页面是不是曾经齐全加载:
WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CLASS_NAME, "home-page-card-container")))
程序里新导入了一些新的模块,最须要留神的就是 WebDriverwait 和 expected_conditions,这两个模块组合起来形成了 Selenium 的 隐式期待(implicit wait)。
隐式期待与显式期待的不同之处在于:隐式期待是等 DOM 中某个状态产生后再持续运行代码(没有明确的等待时间,然而有最大期待时限,只有在时限内就能够),而 显式期待 明确设置了等待时间。在隐式期待中,DOM 触发的状态是用 expected_conditions 定义的(这里导入后用了别名 EC,是常常应用的简称)。在 Selenium 库外面元素被触发的冀望条件(expected condition)有很多种,包含:
- 弹出一个提示框
- 一个元素被选中(比方文本框)
- 页面的题目扭转了,或者某个文字显示在页面上或者某个元素里
- 一个元素在 DOM 中变成可见的,或者一个元素从 DOM 中隐没了
当然,大多数的冀望条件在应用前都须要你先指定期待的指标元素。元素用 定位器(locator)指定。留神,定位器与选择器是不一样的(请看后面对于选择器的介绍)。定位器是一种形象的查询语言,用 By 对象示意,能够用于不同的场合,包含创立选择器。
在上面的示例代码中,一个选择器被用来查找 class 是 title-phone-number 的文本内容:
get_content = driver.find_element(By.CLASS_NAME, "title-phone-number").text
总结
这篇文章次要解说了在 Python 中应用 Selenium 配合 Chrome 浏览器对 JavaScript 生成的内容进行获取,同时如何解决内容是否曾经加载的相干问题,最初解说了 Selenium 如何抉择热面元素等相干内容。
通过下面的文章,咱们应该可能解决一些由 JavaScript 生成的页面内容。