参考:http://cuiqingcai/2599.html
Selenium教程:https://www.yiibai/selenium
selenium 官方参考文档:https://selenium-python.readthedocs.io/index.html
Selenium Documentation:https://www.seleniumhq/docs
selenium模拟登陆, 搜索关键词:https://blog.csdn/u010454729/article/details/51225388
浏览器插件:Selenium IDE
1、Selenium
中式读法:【 瑟林捏幕 】
Selenium( selenium 中文网:http://www.selenium/ )是一个强大的网络数据采集工具,最初是为了网站自动化测试而开发的,被用来测试 Web 应用程序在不同的浏览器和操作系统上运行能力。
Selenium 不带浏览器,它需要与第三方浏览器结合在一起使用。例如,如果你在 Chrome 上运行 Selenium
Selenium 是什么
Selenium 是什么?一句话,自动化测试工具。简单的说就是一个可以用代码操所浏览器的工具。可以通过selenium进行搜索关键字,点击按钮等等操作。它支持各种浏览器,包括 Chrome,Safari,Firefox 等主流界面式浏览器,如果你在这些浏览器里面安装一个 Selenium 的插件,那么便可以方便地实现Web界面的测试。换句话说 Selenium 可以自动化操作这些浏览器,但是必须下载并设置不同的浏览器驱动(注:部分浏览器驱动地址需要科学上网)。
selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium Remote Control)和测试的并行处理(Selenium Grid)。Selenium的核心Selenium Core基于JsUnit,完全由JavaScript编写,因此用于任何支持JavaScript的浏览器上。
selenium 可以模拟真实浏览器,自动化测试工具,支持多种浏览器,爬虫中主要用来解决JavaScript渲染问题。
Selenium 支持多种语言开发,比如 Java,C,Ruby、Python 等。
嗯,所以呢?安装一下 Python 的 Selenium 库,再安装好 PhantomJS,不就可以实现 Python+Selenium+PhantomJS 的无缝对接了嘛!PhantomJS 用来渲染解析JS,Selenium 用来驱动以及与 Python 的对接,Python 进行后期的处理,完美的三剑客!
有人问,为什么不直接用浏览器而用一个没界面的 PhantomJS 呢?答案是:效率高!
用python写爬虫的时候,主要用的是selenium的Webdriver,我们可以通过下面的方式先看看Selenium.Webdriver支持哪些浏览器。首先导入 webdriver 模块。然后使用help函数
from selenium import webdriver
help(webdriver)
安装、使用
首先安装 Selenium:pip install selenium
安装 浏览器 驱动 webdriver
1. chromedriver 下载地址:http://chromedriver.chromium
chromedriver 镜像地址 :CNPM Binaries Mirror
2. Firefox 的驱动 geckodriver 下载地址:Releases · mozilla/geckodriver · GitHub
3. IE 驱动 下载地址:NuGet Gallery | Selenium.WebDriver.IEDriver 4.8.1
注意:下载解压后,将chromedriver.exe , geckodriver.exe , Iedriver.exe 放到 Python 的安装目录,例如 D:\python 。 然后再将 Python 的安装目录添加到系统环境变量的 Path
爬虫 Selenium Chromium 与 Chromedriver对应版本( 注意是 chromium,不是 Chrome ):
淘宝镜像地址在每个文件夹的 notes.txt 中存有 chromium 和 Chromedriver 的版本对应。
from selenium import webdriver
browser = webdriver.Chrome()
# browser = webdriver.Firefox()
# browser = webdriver.Ie()
browser.get('https://www.baidu/')
__input = input("暂停, 按任意键继续")
运行这段代码,会自动打开浏览器,然后访问百度。
如果程序执行错误,浏览器没有打开,那么应该是没有装 Chrome 浏览器或者 Chrome 驱动没有配置在环境变量里。下载驱动,然后将驱动文件路径配置在环境变量即可。
模拟提交
下面的代码实现了“模拟百度提交搜索”的功能。首先等页面加载完成,然后输入关键字到搜索框文本,最后点击提交。
from selenium import webdriver
from selenium.webdrivermon.keys import Keys
from selenium.webdrivermon.by import By
browser = webdriver.Chrome()
browser.get("https://www.baidu")
print(browser.title)
elem = browser.find_element(By.NAME, value="wd") # 百度首页搜索框 name="wd"
elem.send_keys("MM") # 输入关键字
elem.send_keys(Keys.RETURN) # 模拟 点击 enter键,提交
print(browser.page_source) # 打印 js 渲染后的网页
__input = input("暂停, 按enter键退出")
其中 driver.get 方法会打开请求的 URL,WebDriver 会等待页面完全加载完成之后才会返回,即程序会等待页面的所有内容加载完成,JS渲染完毕之后才继续往下执行。注意:如果这里用到了特别多的 Ajax 的话,程序可能不知道是否已经完全加载完毕。
WebDriver 提供了许多寻找网页元素的方法,通过 By 类的方法。
然后,输入文本,模拟点击回车。利用 Keys 这个类来模拟键盘输入。就像敲击键盘一样。
注意:获取网页渲染后的源代码。输出 page_source 属性即可。
通过 dir(browser) 查看,browser 对象有那些方法,
测试用例
import unittest
from selenium import webdriver
from selenium.webdrivermon.keys import Keys
from selenium.webdrivermon.by import By
class PythonOrgSearch(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
def test_search_in_python_org(self):
driver = self.driver
driver.get("https://www.python")
self.assertIn("Python", driver.title)
elem = driver.find_element(By.NAME, value="q")
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
assert "No results found." not in driver.page_source
def tearDown(self):
self.driver.close()
if __name__ == "__main__":
unittest.main()
测试用例是继承了 unittest.TestCase 类,继承这个类表明这是一个测试类。setUp方法是初始化的方法,这个方法会在每个测试类中自动调用。每一个测试方法命名都有规范,必须以 test 开头,会自动执行。最后的 tearDown 方法会在每一个测试方法结束之后调用。这相当于最后的析构方法。在这个方法里写的是 close 方法,你还可以写 quit 方法。不过 close 方法相当于关闭了这个 TAB 选项卡,然而 quit 是退出了整个浏览器。当你只开启了一个 TAB 选项卡的时候,关闭的时候也会将整个浏览器关闭。
import time
from selenium import webdriver
from selenium.webdrivermon.by import By
browser = webdriver.Chrome()
browser.maximize_window() # 最大化浏览器
browser.implicitly_wait(20) # 设置隐式时间等待
url = 'https://www.baidu'
browser.get(url)
# 网页的登录按钮
btn_login = browser.find_element(By.ID, value='s-top-loginbtn')
btn_login.click() # 点击登录按钮
time.sleep(2) # 显式等待 2s
# 查找所有的 a 标签
all_link = browser.find_element(By.TAG_NAME, value='a')
link = all_link[3] # 提取 第四个 a 标签
link.click() # 点击 提取 的 a 标签
input('暂停,按 enter 退出...')
页面操作
访问页面
运行后自动打开Chrome浏览器,并登陆百度打印百度首页的源代码,然后关闭浏览器
import time
from selenium import webdriver
from selenium.webdrivermon.by import By
browser = webdriver.Chrome()
browser.get("https://www.baidu")
print(browser.page_source)
browser.close()
执行JavaScript
这是一个非常有用的方法,这里就可以直接调用js方法来实现一些操作,下面的例子是通过登录知乎然后通过js翻到页面底部,并弹框提示
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.zhihu/explore")
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
browser.execute_script('alert("To Bottom")')
input('暂停,按 enter 退出')
browser.close()
查找 ( 定位 ) 元素
Selenium常见元素定位方法和操作的学习介绍:[python爬虫] Selenium常见元素定位方法和操作的学习介绍_selenium元素定位_Eastmount的博客-CSDN博客
Selenium切换窗口句柄及调用Chrome浏览器:[python爬虫] Selenium切换窗口句柄及调用Chrome浏览器_selenium 窗口句柄_Eastmount的博客-CSDN博客
查找单个元素
import time
from selenium import webdriver
from selenium.webdrivermon.by import By
browser = webdriver.Chrome()
browser.get("https://www.taobao")
input_first = browser.find_element(By.ID, "q")
print(input_first)
input_second = browser.find_element(By.CSS_SELECTOR, "#q")
print(input_second)
input_third = browser.find_element(By.XPATH, '//*[@id="q"]')
print(input_third)
browser.close()
这里通过三种不同的方式获取元素。结果都是相同的。
查找元素的方法
Selenium 提供了8种定位方式:id、name、class name、tag name、link text、partial link text、xpath、css selector。
By 类的一些属性如下
ID = "id"
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"
通过 By 模块 定位元素
from selenium import webdriver
from selenium.webdrivermon.by import By
browser = webdriver.Chrome()
browser.get("https://www.taobao")
input_first = browser.find_element(By.ID, "q")
print(input_first)
browser.close()
多个元素查找
- 单个元素是 find_element,
- 多个元素是 find_elements,结果是获得一个元素列表
示例:lis = browser.find_elements(By.CSS_SELECTOR,'.service-bd li')
页面交互 ( 点击、输入 )
玩转python selenium鼠标键盘操作(ActionChains):玩转python selenium鼠标键盘操作(ActionChains)_python_脚本之家
Selenium鼠标与键盘事件常用操作方法示例:Selenium鼠标与键盘事件常用操作方法示例_python_脚本之家
找到页面元素后,就需要和页面交互,比如:点击,输入等等。
需要导入包:from selenium.webdrivermon.keys import Keys
ele.send_keys("some text") # 文本框输入
ele.send_keys("and some", Keys.ARROW_DOWN)
ele.clear() # 清除 文本框 内容
Selenium 所有的 api 文档:7. WebDriver API — Selenium Python Bindings 2 documentation
交互动作 --- 动作链 (拖动、移动等)
将动作附加到动作链中串行执行
from selenium import webdriver
from selenium.webdrivermon.by import By
from selenium.webdrivermon.keys import Keys
from selenium.webdriver import ActionChains
browser = webdriver.Chrome()
url = "https://www.runoob/try/try.php?filename=jqueryui-api-droppable"
browser.get(url)
browser.switch_to.frame('iframeResult')
source = browser.find_element(By.CSS_SELECTOR, '#draggable')
target = browser.find_element(By.CSS_SELECTOR, '#droppable')
actions = ActionChains(browser)
actions.drag_and_drop(source, target)
actions.perform()
actions.drag_and_drop_by_offset(source, 400, 0).perform()
input('暂停,按 enter 退出')
browser.close()
更多操作参考:7. WebDriver API — Selenium Python Bindings 2 documentation
元素拖拽
要完成元素的拖拽,首先你需要指定被拖动的元素和拖动目标元素,然后利用 ActionChains 类来实现
element = driver.find_element_by_name("source")
target = driver.find_element_by_name("target")
from selenium.webdriver import ActionChains
action_chains = ActionChains(driver)
action_chains.drag_and_drop(element, target).perform()
actions.drag_and_drop_by_offset(element, 400, 0).perform()
这样就实现了元素从 source 拖动到 target 的操作
chrome 浏览器打开标签页的快捷键是 ctrl+t,那把 ctrl+t 的按键事件传入即可
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdrivermon.by import By
from selenium.webdrivermon.keys import Keys
from selenium.webdriver import ActionChainsbrowser = webdriver.Chrome()
browser.maximize_window()
url = 'https://www.baidu'
browser.get(url)ac = ActionChains(browser)
ac.key_down(Keys.CONTROL).key_down('t').key_up(Keys.CONTROL).key_up('t').perform()
input("暂停,按 enter 退出...")
browser.close()
import time
from selenium import webdriver
from selenium.webdrivermon.keys import Keys
from selenium.webdrivermon.by import By
browser = webdriver.Chrome() # 默认的火狐浏览器
for i in range(5):
# 这句代码相当于在浏览器窗口下按下ctrl+t打开一个新的标签页
browser.find_element(By.TAG_NAME, 'body').send_keys(Keys.CONTROL + 't')
time.sleep(10) # 等待所有窗口完全打开,10秒够用了, 如果不打开得不到所有句柄,只能得到部分。
handles = browser.window_handles
print(len(handles))
print(handles)
input('暂停,按 enter 退出')
browser.close()
通常要确保页面加载完成,可以使用 selenium.webdriver.support.ui.WebDriverWait
打开新窗口示例代码:
from selenium import webdriver
# 打开谷歌浏览器
browser = webdriver.Chrome()
# 打开窗口
browser.get("https://www.baidu/")
# 打开新窗口
newwindow_js = 'window.open("https://www.baidu");' # js
browser.execute_script(newwindow_js) # 执行 js 打开新的页面
# 切换到新的窗口
handles = browser.window_handles # 得到 所有页面 句柄
browser.switch_to_window(handles[-1]) # 切换焦点到 对应页面
获取元素属性
get_attribute('class')
from selenium import webdriver
from selenium.webdrivermon.by import By
browser = webdriver.Chrome()
url = 'https://www.zhihu/explore'
browser.get(url)
logo = browser.find_element(By.ID, 'zh-top-link-logo')
print(logo)
print(logo.get_attribute('class'))
browser.close()
获取 文本值、ID、位置、标签名
id、location、tag_name、size
from selenium import webdriver
from selenium.webdrivermon.by import By
browser = webdriver.Chrome()
url = 'https://www.zhihu/explore'
browser.get(url)
tag_input = browser.find_element(By.CLASS_NAME, 'zu-top-add-question')
print(tag_input.text)
print(tag_input.id)
print(tag_input.location)
print(tag_input.tag_name)
print(tag_input.size)
下拉 选项卡
下拉选项卡的的处理
tag_select = browser.find_element(By.XPATH, "//select[@name='name']")
all_options = tag_select.find_elements(By.TAG_NAME, "option")
for option in all_options:
print(f'Value is: {option.get_attribute("value")}')
option.click()
首先获取了第一个 select 元素,也就是下拉选项卡。然后轮流设置了 select 选项卡中的每一个 option 选项。你可以看到,这并不是一个非常有效的方法
其实 WebDriver 中提供了一个叫 Select 的方法,可以帮助我们完成这些事情。
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element_by_name('name'))
select.select_by_index(index)
select.select_by_visible_text("text")
select.select_by_value(value)
如你所见,它可以根据索引来选择,可以根据值来选择,可以根据文字来选择。是十分方便的。
全部取消选择怎么办呢?很简单
select = Select(driver.find_element_by_id('id'))
select.deselect_all()
这样便可以取消所有的选择。
另外我们还可以通过下面的方法获取所有的已选选项。
select = Select(driver.find_element_by_xpath("xpath"))
all_selected_options = select.all_selected_options
获取所有可选选项是
options = select.options
如果你把表单都填好了,最后肯定要提交表单对吧。怎吗提交呢?很简单
driver.find_element_by_id("submit").click()
这样就相当于模拟点击了 submit 按钮,做到表单提交。
当然你也可以单独提交某个元素
element.submit()
方法,WebDriver 会在表单中寻找它所在的表单,如果发现这个元素并没有被表单所包围,那么程序会抛出 NoSuchElementException 的异常。
Frame
在很多网页中都是有Frame标签,所以我们爬取数据的时候就涉及到切入到frame中以及切出来的问题,通过下面的例子演示
这里常用的是 switch_to.from() 和 switch_to.parent_frame()
import time
from selenium import webdriver
from seleniummon.exceptions import NoSuchElementException
browser = webdriver.Chrome()
url = 'http://www.runoob/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
source = browser.find_element_by_css_selector('#draggable')
print(source)
try:
logo = browser.find_element_by_class_name('logo')
except NoSuchElementException:
print('NO LOGO')
browser.switch_to.parent_frame()
logo = browser.find_element_by_class_name('logo')
print(logo)
print(logo.text)
页面切换 和 打开新窗口
Python+Selenium练习篇之27-多窗口之间切换 :Python+Selenium练习篇之27-多窗口之间切换_Anthony_tester的博客-CSDN博客
python selenium打开新窗口,多窗口切换:python selenium打开新窗口,多窗口切换_蘑菇猎手的博客-CSDN博客
python/selenium/chrome打开新窗口并实现窗口切换: python + selenium + chrome 如何打开新窗口,并实现窗口切换_谷歌浏览器新窗口代码_Kosmoo的博客-CSDN博客selenium之 定位以及切换frame(iframe):selenium之 定位以及切换frame(iframe)_selenium切换到iframe_huilan_same的博客-CSDN博客
关键字:selenium iframe 切换
如果在一个页面上点击一个链接之后,并不是在当前页面上打开,而是重新打开一个新页面;这种情况下如何跳转到新的页面上操作?首先,需要了解的是每个窗口都有句柄的,可以理解为浏览器窗口的唯一标识符,根据这个标识符来确定新打开的窗口。打开新页面后,selenium 的 focus 还是在 原来的页面上,所以需要使用 switch_to.window 方法把 焦点(focus) 切换到新页面上
如果是新打开的 iframe 就使用 switch_to_frame('xxx')
如果是新打开的 tab 就使用 switch_to_window('')
一个浏览器肯定会有很多窗口,所以我们肯定要有方法来实现窗口的切换。切换窗口的方法如下
driver.switch_to_window("windowName")
switch_to_window 方法现在已经废弃,鼠标放在这个方法上提示 使用 switch_to.window 代替
另外你可以使用 window_handles 方法来获取每个窗口的操作对象。例如
for handle in driver.window_handles:
driver.switch_to_window(handle)
另外切换 frame 的方法如下
driver.switch_to_frame("frameName.0.child")
这样焦点会切换到一个 name 为 child 的 frame 上。
打开新窗口主要使用 JavaScript 实现:
# 新标签页打开这个url
js="window.open("url")"
driver.execute_script(js)
time.sleep(2)
选项卡 管理
通过执行 js 命令实现新开选项卡 window.open()
不同的选项卡是存在列表里 browser.window_handles
通过 browser.window_handles[0] 就可以操作第一个选项卡
import time
from selenium import webdriver
from selenium.webdrivermon.keys import Keys
from selenium.webdrivermon.by import By
browser = webdriver.Chrome()
browser.get('https://www.baidu')
browser.execute_script('window.open()')
print(browser.window_handles)
browser.switch_to.window(browser.window_handles[1])
browser.get('https://www.taobao')
time.sleep(1)
browser.switch_to.window(browser.window_handles[0])
browser.get('https://python')
input('暂停, 按enter退出...')
browser.close()
弹窗处理
页面出现了弹窗
alert = driver.switch_to_alert()
通过上述方法可以获取弹窗对象。
历史记录 ( 前进、后退 )
前进 和 后退 针对的是 浏览器浏览的网页 的 历史记录
driver.forward()
driver.back()
Cookies 处理
get_cookies()
delete_all_cookes()
add_cookie()
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.zhihu/explore')
print(browser.get_cookies())
browser.add_cookie({'name': 'name', 'domain': 'www.zhihu', 'value': 'zhaofan'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())
为页面添加 Cookies,用法如下
# Go to the correct domain
driver.get("http://www.example")
# Now set the cookie. This one's valid for the entire domain
cookie = {‘name’ : ‘foo’, ‘value’ : ‘bar’}
driver.add_cookie(cookie)
获取页面 Cookies,用法如下
# Go to the correct domain
driver.get("http://www.example")
# And now output all the available cookies for the current URL
driver.get_cookies()
以上便是 Cookies 的处理,同样是非常简单的。
页面 ( 隐式、显示) 等待
Python selenium 三种等待方式详解(必会):Python selenium 三种等待方式详解(必会)_python_脚本之家
这是非常重要的一部分,现在的网页越来越多采用了 Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。这会让元素定位困难而且会提高产生 ElementNotVisibleException 的概率。所以 Selenium 提供了两种等待方式,
- 隐式等待:等待特定的时间。如果 WebDriver没有在 DOM中找到元素,将继续等待,超出设定时间后则抛出找不到元素的异常。默认的时间是0
- 显式等待:首先指定一个等待条件,并且再指定一个最长等待时间,然后在这个时间段内进行判断是否满足等待条件,如果成立就会立即返回,如果不成立,就会一直等待,直到等待你指定的最长等待时间,如果还是不满足,就会抛出异常,如果满足了就会正常返回
from selenium import webdriver
from selenium.webdrivermon.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("http://somedomain/url_that_delays_loading")
try:
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "myDynamicElement"))
)
finally:
driver.quit()
程序默认会 500ms 调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回。
from selenium import webdriver
from selenium.webdrivermon.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
browser = webdriver.Chrome()
browser.get('https://www.taobao/')
wait = WebDriverWait(browser, 10)
tag_input = wait.until(EC.presence_of_element_located((By.ID, 'q')))
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search')))
print(input, button)
上述的例子中的条件:EC.presence_of_element_located()是确认元素是否已经出现了
EC.element_to_be_clickable()是确认元素是否是可点击的
下面是一些内置的等待条件,你可以直接调用这些条件,而不用自己写某些等待条件了。
常用的判断条件:
title_is 标题是某内容
title_contains 标题包含某内容
presence_of_element_located 元素加载出,传入定位元组,如(By.ID, 'p')
visibility_of_element_located 元素可见,传入定位元组
visibility_of 可见,传入元素对象
presence_of_all_elements_located 所有元素加载出
text_to_be_present_in_element 某个元素文本包含某文字
text_to_be_present_in_element_value 某个元素值包含某文字
frame_to_be_available_and_switch_to_it frame 加载并切换
invisibility_of_element_located 元素不可见
element_to_be_clickable 元素可点击
staleness_of 判断一个元素是否仍在DOM,可判断页面是否已经刷新
element_to_be_selected 元素可选择,传元素对象
element_located_to_be_selected 元素可选择,传入定位元组
element_selection_state_to_be 传入元素对象以及状态,相等返回True,否则返回False
element_located_selection_state_to_be 传入定位元组以及状态,相等返回True,否则返回False
alert_is_present 是否出现Alert
更多操作参考:7. WebDriver API — Selenium Python Bindings 2 documentation
隐式等待
隐式等待比较简单,就是简单地设置一个等待时间,单位为秒。到了一定的时间发现元素还没有加载,则继续等待我们指定的时间,如果超过了我们指定的时间还没有加载就会抛出异常,如果没有需要等待的时候就已经加载完毕就会立即执行
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")
当然如果不设置,默认等待时间为0。
from selenium import webdriver
browser = webdriver.Chrome()
browser.implicitly_wait(10)
browser.get('https://www.zhihu/explore')
input = browser.find_element_by_class_name('zu-top-add-question')
print(input)
异常处理
这里的异常比较复杂,官网的参考地址:
7. WebDriver API — Selenium Python Bindings 2 documentation
这里只进行简单的演示,查找一个不存在的元素
from selenium import webdriver
from seleniummon.exceptions import TimeoutException, NoSuchElementException
browser = webdriver.Chrome()
try:
browser.get('https://www.baidu')
except TimeoutException:
print('Time Out')
try:
browser.find_element_by_id('hello')
except NoSuchElementException:
print('No Element')
finally:
browser.close()
2、PhantomJS
************************* PhantomJS 已经停止更新 *************************
中式读法:【 饭特姆JS 】
PhantomJS(官网: http://phantomjs/ )是一个基于 WebKit 内核、无 UI 界面的浏览器,WebKit 是一个开源的浏览器引擎。比如,主流的 Safari、Google Chrome、傲游3、猎豹浏览器、百度浏览器、opera浏览器 都是基于 Webkit 开发。)
PhantomJS 会把网站数据加载到内存中,并执行页面上的 JavaScript,但不会向用户展示图形界面。
PhantomJS 是一个无界面的,可脚本编程的WebKit浏览器引擎。它原生支持多种web 标准:DOM 操作,CSS选择器,JSON,Canvas 以及SVG。
官方网站:http://phantomjs/download.html
Examples:http://phantomjs/examples/index.html
安装完成之后命令行输入:phantomjs -v
如果正常显示版本号,那么证明安装成功了。如果提示错误,那么请重新安装。
快速开始
第一个程序
第一个程序当然是Hello World,新建一个 js 文件。命名为 helloworld.js
console.log('Hello, world!');
phantom.exit();
命令行输入
phantomjs helloworld.js
程序输出了 Hello,world!程序第二句话终止了 phantom 的执行。
注意:phantom.exit();这句话非常重要,否则程序将永远不会终止。
页面加载
可以利用 phantom 来实现页面的加载,下面的例子实现了页面的加载并将页面保存为一张图片。
var page = require('webpage').create();
page.open('http://example', function (status) {
console.log("Status: " + status);
if (status === "success") {
page.render('example.png');
}
phantom.exit();
});
首先创建了一个webpage对象,然后加载本站点主页,判断响应状态,如果成功,那么保存截图为 example.png
以上代码命名为 pageload.js,命令行
phantomjs pageload.js
发现执行成功,然后目录下多了一张图片,example.png
因为这个 render 方法,phantom 经常会用到网页截图的功能。
测试页面加载速度
下面这个例子计算了一个页面的加载速度,同时还用到了命令行传参的特性。新建文件保存为 loadspeed.js
var page = require('webpage').create(),
system = require('system'),
t, address;
if (system.args.length === 1) {
console.log('Usage: loadspeed.js <some URL>');
phantom.exit();
}
t = Date.now();
address = system.args[1];
page.open(address, function(status) {
if (status !== 'success') {
console.log('FAIL to load the address');
} else {
t = Date.now() - t;
console.log('Loading ' + system.args[1]);
console.log('Loading time ' + t + ' msec');
}
phantom.exit();
});
程序判断了参数的多少,如果参数不够,那么终止运行。然后记录了打开页面的时间,请求页面之后,再纪录当前时间,二者之差就是页面加载速度。
phantomjs loadspeed.js http://example
运行结果
Loading http://example
Loading time 11678 msec
这个时间包括JS渲染的时间,当然和网速也有关。
代码评估
利用 evaluate 方法我们可以获取网页的源代码。这个执行是“沙盒式”的,它不会去执行网页外的 JavaScript 代码。evalute 方法可以返回一个对象,然而返回值仅限于对象,不能包含函数(或闭包)
var url = 'http://www.qq';
var page = require('webpage').create();
page.open(url, function(status) {
var title = page.evaluate(function() {
return document.title;
});
console.log('Page title is ' + title);
phantom.exit();
});
以上代码获取了腾讯的网站标题。
Page title is 腾讯首页
任何来自于网页并且包括来自 evaluate() 内部代码的控制台信息,默认不会显示。
需要重写这个行为,使用 onConsoleMessage 回调函数,示例可以改写成
var url = 'http://www.itcast/';
var page = require('webpage').create();
page.onConsoleMessage = function (msg) {
console.log(msg);
};
page.open(url, function (status) {
page.evaluate(function () {
console.log(document.title);
});
phantom.exit();
});
页面自动化处理
- DOM操作
脚本都是像在浏览器中运行的,所以标准的 JavaScript 的 DOM 操作和 CSS 选择器也是生效的。
例如下面的例子就修改了 User-Agent,然后还返回了页面中某元素的内容。
var url = 'http://www.httpuseragent';
var page = require('webpage').create();
console.log('The default user agent is ' + page.settings.userAgent);
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36';
page.open('http://www.httpuseragent', function (status) {
if (status !== 'success') {
console.log('Unable to access network');
}
else {
var ua = page.evaluate(function () {
return document.getElementById('myagent').innerText;
});
console.log(ua);
}
phantom.exit();
});
运行结果
The default user agent is Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34
Your Http User Agent string is: SpecialAgent
首先打印出了默认的 User-Agent,然后通过修改它,请求验证 User-Agent 的一个站点,通过选择器得到了修改后的 User-Agent。
3、pyppeteer:比 selenium 更高效的爬虫利器
pyppeteer API Reference:https://miyakogi.github.io/pyppeteer/reference.html
pyppeteer github 地址:https://github/miyakogi/pyppeteer
pyppeteer 英文文档地址:https://miyakogi.github.io/pyppeteer/
puppeteer 快速入门:https://blog.csdn/freeking101/article/details/91542887
pyppeteer 进阶技巧:https://wwwblogs/dyfblog/p/10887940.html
爬虫、获取cookie、截屏插件、防爬绕过:https://mohen.blog.csdn/article/details/107312709
爬虫神器 Pyppeteer 的使用:https://blog.csdn/weixin_38819889/article/details/108684254
Pyppeteer 这个项目是非官方的,是基于谷歌官方puppeteer的python版本。chrome 就问题多多,puppeteer也是各种坑,加上pyppeteer是前两者的python版本,也就是产生了只要前两个有一个有bug,那么pyppeteer就会原封不动的继承下来,本来这没什么,但是现在遇到的问题就是 pyppeteer 这个项目已经停止更新,导致很多 bug 根本没人修复。所以,Pyppeteer 已经停止更新,可以使用 playwright-python 代替。
selenium 作为一款知名的 Web 自动化测试框架,selenium 支持多款主流浏览器,提供了功能丰富的API 接口,经常被我们用作爬虫工具来使用。但是 selenium 的缺点也很明显,比如速度太慢、对版本配置要求严苛,最麻烦是经常要更新对应的驱动。还有些网页是可以检测到是否是使用了selenium 。并且selenium 所谓的保护机制不允许跨域 cookies 保存以及登录的时候必须先打开网页然后后加载 cookies 再刷新的方式很不友好。
介绍 Pyppeteer 之前先说一下 Puppeteer,Puppeteer 是 Google 基于 Node.js 开发的一个工具,主要是用来操纵 Chrome 浏览器的 API,通过 Javascript 代码来操纵 Chrome 浏览器的一些操作,用作网络爬虫完成数据爬取、Web 程序自动测试等任务。其 API 极其完善,功能非常强大。 而 Pyppeteer 又是什么呢?它实际上是 Puppeteer 的 Python 版本的实现,但他不是 Google 开发的,是一位来自于日本的工程师依据 Puppeteer 的一些功能开发出来的非官方版本。
Pyppeteer 的两大特点:chromium 浏览器 和 asyncio框架。
Pyppeteer 其实是 Puppeteer 的 Python 版本。pyppeteer 模块看不懂就去看puppeteer文档,pyppeteer 只是在 puppeteer之上稍微包装了下而已 。
安装 pyppeteer 库:pip3 install pyppeteer 至于 chromium 浏览器,只需要一条 pyppeteer-install 命令就会自动下载对应的最新版本 chromium 浏览器到 pyppeteer 的默认位置。
window 下 安装完 pyppeteer ,会在 python 安装目录下的 Scripts 目录下 有 pyppeteer-install.exe 和 pyppeteer-install-script.py 两个文件,执行 任意一个都可以安装 chromium 浏览器到 pyppeteer 的默认位置。
如果不运行 pyppeteer-install 命令,在第一次使用 pyppeteer 的时候也会自动下载并安装 chromium 浏览器,效果是一样的。总的来说,pyppeteer 比起 selenium 省去了 driver 配置的环节。
当然,出于某种原因(需要梯子,或者科学上网),也可能会出现chromium自动安装无法顺利完成的情况,这时可以考虑手动安装:首先,从下列网址中找到自己系统的对应版本,下载chromium压缩包;
'linux': 'https://storage.googleapis/chromium-browser-snapshots/Linux_x64/575458/chrome-linux.zip'
'mac': 'https://storage.googleapis/chromium-browser-snapshots/Mac/575458/chrome-mac.zip'
'win32': 'https://storage.googleapis/chromium-browser-snapshots/Win/575458/chrome-win32.zip'
'win64': 'https://storage.googleapis/chromium-browser-snapshots/Win_x64/575458/chrome-win32.zip'
然后,将压缩包放到 pyppeteer 的指定目录下解压缩,windows 系统的默认目录。
其他系统下的默认目录可以参照下面:
- Windows: C:\Users\<username>\AppData\Local\pyppeteer
- OS X: /Users/<username>/Library/Application Support/pyppeteer
- Linux: /home/<username>/.local/share/pyppeteer
or in $XDG_DATA_HOME/pyppeteer if $XDG_DATA_HOME is defined.
Details see appdirs’s user_data_dir
.
好了,安装完成之后我们命令行下测试下:
>>> import pyppeteer
如果没有报错,那么就证明安装成功了。
示例:
执行代码后,手动输入用户名和密码,滑动滑块,可以正常跳转到登录后个人页面。
提示:这个手动滑动滑块有一定的失败几率,有时候失败几率还很高。有时一次就可以滑过,有时好多次都过不去。
示例代码:
import asyncio
from pyppeteer import launch
width, height = 1366, 768
js1 = '''() =>{Object.defineProperties(navigator,{ webdriver:{ get: () => false}})}'''
js2 = '''() => {alert(window.navigator.webdriver)}'''
js3 = '''() => {window.navigator.chrome = {runtime: {}, }; }'''
js4 = '''() =>{Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});}'''
js5 = '''() =>{Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5,6],});}'''
async def page_evaluate(page):
# 替换淘宝在检测浏览时采集的一些参数
# 需要注意,在测试的过程中发现登陆成功后页面的该属性又会变成True
# 所以在每次重新加载页面后要重新设置该属性的值。
await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {}, }; }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
async def main():
browser = await launch(
headless=False,
# userDataDir='./userdata',
args=['--disable-infobars', f'--window-size={width},{height}', '--no-sandbox']
)
page = await browser.newPage()
await page.setViewport(
{
"width": width,
"height": height
}
)
# url = 'https://www.taobao'
url = 'https://login.taobao/member/login.jhtml'
await page.goto(url=url)
await page.evaluate(js1)
await page.evaluate(js3)
await page.evaluate(js4)
await page.evaluate(js5)
# await page_evaluate(page)
await asyncio.sleep(100)
# await browser.close()
asyncio.get_event_loop().run_until_complete(main())
基础用法
抓取内容 可以使用 xpath 表达式
"""
# Pyppeteer 三种解析方式
Page.querySelector() # 选择器
Page.querySelectorAll()
Page.xpath() # xpath 表达式
# 简写方式为:
Page.J(), Page.JJ(), and Page.Jx()
"""
import asyncio
from pyppeteer import launch
async def main():
# headless参数设为False,则变成有头模式
# Pyppeteer支持字典和关键字传参,Puppeteer只支持字典传参
# 指定引擎路径
# exepath = r'C:\Users\Administrator\AppData\Local\pyppeteer\pyppeteer\local-chromium\575458\chrome-win32/chrome.exe'
# browser = await launch({'executablePath': exepath, 'headless': False, 'slowMo': 30})
browser = await launch(
# headless=False,
{'headless': False}
)
page = await browser.newPage()
# 设置页面视图大小
await page.setViewport(viewport={'width': 1280, 'height': 800})
# 是否启用JS,enabled设为False,则无渲染效果
await page.setJavaScriptEnabled(enabled=True)
# 超时间见 1000 毫秒
res = await page.goto('https://www.toutiao/', options={'timeout': 1000})
resp_headers = res.headers # 响应头
resp_status = res.status # 响应状态
# 等待
await asyncio.sleep(2)
# 第二种方法,在while循环里强行查询某元素进行等待
while not await page.querySelector('.t'):
pass
# 滚动到页面底部
await page.evaluate('window.scrollBy(0, document.body.scrollHeight)')
await asyncio.sleep(2)
# 截图 保存图片
await page.screenshot({'path': 'toutiao.png'})
# 打印页面cookies
print(await page.cookies())
""" 打印页面文本 """
# 获取所有 html 内容
print(await page.content())
# 在网页上执行js 脚本
dimensions = await page.evaluate(pageFunction='''() => {
return {
width: document.documentElement.clientWidth, // 页面宽度
height: document.documentElement.clientHeight, // 页面高度
deviceScaleFactor: window.devicePixelRatio, // 像素比 1.0000000149011612
}
}''', force_expr=False) # force_expr=False 执行的是函数
print(dimensions)
# 只获取文本 执行 js 脚本 force_expr 为 True 则执行的是表达式
content = await page.evaluate(pageFunction='document.body.textContent', force_expr=True)
print(content)
# 打印当前页标题
print(await page.title())
# 抓取新闻内容 可以使用 xpath 表达式
"""
# Pyppeteer 三种解析方式
Page.querySelector() # 选择器
Page.querySelectorAll()
Page.xpath() # xpath 表达式
# 简写方式为:
Page.J(), Page.JJ(), and Page.Jx()
"""
element = await page.querySelector(".feed-infinite-wrapper > ul>li") # 纸抓取一个
print(element)
# 获取所有文本内容 执行 js
content = await page.evaluate('(element) => element.textContent', element)
print(content)
# elements = await page.xpath('//div[@class="title-box"]/a')
elements = await page.querySelectorAll(".title-box a")
for item in elements:
print(await item.getProperty('textContent'))
# <pyppeteer.execution_context.JSHandle object at 0x000002220E7FE518>
# 获取文本
title_str = await (await item.getProperty('textContent')).jsonValue()
# 获取链接
title_link = await (await item.getProperty('href')).jsonValue()
print(title_str)
print(title_link)
# 关闭浏览器
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
import asyncio
import pyppeteer
from collections import namedtuple
headers = {
'date': 'Sun, 28 Apr 2019 06:50:20 GMT',
'server': 'Cmcc',
'x-frame-options': 'SAMEORIGIN\nSAMEORIGIN',
'last-modified': 'Fri, 26 Apr 2019 09:58:09 GMT',
'accept-ranges': 'bytes',
'cache-control': 'max-age=43200',
'expires': 'Sun, 28 Apr 2019 18:50:20 GMT',
'vary': 'Accept-Encoding,User-Agent',
'content-encoding': 'gzip',
'content-length': '19823',
'content-type': 'text/html',
'connection': 'Keep-alive',
'via': '1.1 ID-0314217270751344 uproxy-17'
}
Response = namedtuple("rs", "title url html cookies headers history status")
async def get_html(url):
browser = await pyppeteer.launch(headless=True, args=['--no-sandbox'])
page = await browser.newPage()
res = await page.goto(url, options={'timeout': 10000})
data = await page.content()
title = await page.title()
resp_cookies = await page.cookies() # cookie
resp_headers = res.headers # 响应头
resp_status = res.status # 响应状态
print(data)
print(title)
print(resp_headers)
print(resp_status)
return title
if __name__ == '__main__':
url_list = [
"https://www.toutiao",
"http://jandan/ooxx/page-8#comments",
"https://www.12306/index"
]
task = [get_html(url) for url in url_list]
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*task))
for res in results:
print(res)
模拟输入
模拟输入文本:
# 模拟输入 账号密码 {'delay': rand_int()} 为输入时间
await page.type('#TPL_username_1', "sadfasdfasdf")
await page.type('#TPL_password_1', "123456789", )
await page.waitFor(1000)
await page.click("#J_SubmitStatic")
使用 tkinter 获取页面高度 宽度
def screen_size():
"""使用tkinter获取屏幕大小"""
import tkinter
tk = tkinter.Tk()
width = tk.winfo_screenwidth()
height = tk.winfo_screenheight()
tk.quit()
return width, height
爬取京东商城
示例代码:
import requests
from bs4 import BeautifulSoup
from pyppeteer import launch
import asyncio
def screen_size():
"""使用tkinter获取屏幕大小"""
import tkinter
tk = tkinter.Tk()
width = tk.winfo_screenwidth()
height = tk.winfo_screenheight()
tk.quit()
return width, height
async def main(url):
# browser = await launch({'headless': False, 'args': ['--no-sandbox'], })
browser = await launch({'args': ['--no-sandbox'], })
page = await browser.newPage()
width, height = screen_size()
await page.setViewport(viewport={"width": width, "height": height})
await page.setJavaScriptEnabled(enabled=True)
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
)
await page.goto(url)
# await asyncio.sleep(2)
await page.evaluate('window.scrollBy(0, document.body.scrollHeight)')
await asyncio.sleep(1)
# content = await page.content()
li_list = await page.xpath('//*[@id="J_goodsList"]/ul/li')
# print(li_list)
item_list = []
for li in li_list:
a = await li.xpath('.//div[@class="p-img"]/a')
detail_url = await (await a[0].getProperty("href")).jsonValue()
promo_words = await (await a[0].getProperty("title")).jsonValue()
a_ = await li.xpath('.//div[@class="p-commit"]/strong/a')
p_commit = await (await a_[0].getProperty("textContent")).jsonValue()
i = await li.xpath('./div/div[3]/strong/i')
price = await (await i[0].getProperty("textContent")).jsonValue()
em = await li.xpath('./div/div[4]/a/em')
title = await (await em[0].getProperty("textContent")).jsonValue()
item = {
"title": title,
"detail_url": detail_url,
"promo_words": promo_words,
'p_commit': p_commit,
'price': price
}
item_list.append(item)
# print(item)
# break
# print(content)
await page_close(browser)
return item_list
async def page_close(browser):
for _page in await browser.pages():
await _page.close()
await browser.close()
msg = "手机"
url = "https://search.jd/Search?keyword={}&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq={}&cid2=653&cid3=655&page={}"
task_list = []
for i in range(1, 6):
page = i * 2 - 1
url = url.format(msg, msg, page)
task_list.append(main(url))
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*task_list))
# print(results, len(results))
for i in results:
print(i, len(i))
print('*' * 100)
# soup = BeautifulSoup(content, 'lxml')
# div = soup.find('div', id='J_goodsList')
# for i, li in enumerate(div.find_all('li', class_='gl-item')):
# if li.select('.p-img a'):
# print(li.select('.p-img a')[0]['href'], i)
# print(li.select('.p-price i')[0].get_text(), i)
# print(li.select('.p-name em')[0].text, i)
# else:
# print("#" * 200)
# print(li)
抓取淘宝
示例代码:
# -*- coding: utf-8 -*-
import time
import random
import asyncio
from retrying import retry # 错误自动重试
from pyppeteer.launcher import launch
js1 = '''() =>{Object.defineProperties(navigator,{ webdriver:{ get: () => false}})}'''
js2 = '''() => {alert(window.navigator.webdriver)}'''
js3 = '''() => {window.navigator.chrome = {runtime: {}, }; }'''
js4 = '''() =>{Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});}'''
js5 = '''() =>{Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5,6],});}'''
def retry_if_result_none(result):
return result is None
@retry(retry_on_result=retry_if_result_none, )
async def mouse_slide(page=None):
await asyncio.sleep(3)
try:
await page.hover('#nc_1_n1z')
await page.mouse.down()
await page.mouse.move(2000, 0, {'delay': random.randint(1000, 2000)})
await page.mouse.up()
except Exception as e:
print(e, ' :slide login False')
return None
else:
await asyncio.sleep(3)
slider_again = await page.Jeval('.nc-lang-cnt', 'node => node.textContent')
if slider_again != '验证通过':
return None
else:
await page.screenshot({'path': './headless-slide-result.png'})
print('验证通过')
return 1
def input_time_random():
return random.randint(100, 151)
def screen_size():
"""使用tkinter获取屏幕大小"""
import tkinter
tk = tkinter.Tk()
width = tk.winfo_screenwidth()
height = tk.winfo_screenheight()
tk.quit()
return width, height
async def main(username, pwd, url):
browser = await launch(
{'headless': False, 'args': ['--no-sandbox'], },
userDataDir='./userdata',
args=['--window-size=1366,768']
)
page = await browser.newPage()
width, height = screen_size()
await page.setViewport(viewport={"width": width, "height": height})
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
)
await page.goto(url)
await page.evaluate(js1)
await page.evaluate(js3)
await page.evaluate(js4)
await page.evaluate(js5)
pwd_login = await page.querySelector('.J_Quick2Static')
# print(await (await pwd_login.getProperty('textContent')).jsonValue())
await pwd_login.click()
await page.type('#TPL_username_1', username, {'delay': input_time_random() - 50})
await page.type('#TPL_password_1', pwd, {'delay': input_time_random()})
await page.screenshot({'path': './headless-test-result.png'})
time.sleep(2)
slider = await page.Jeval('#nocaptcha', 'node => node.style') # 是否有滑块
if slider:
print('出现滑块情况判定')
await page.screenshot({'path': './headless-login-slide.png'})
flag = await mouse_slide(page=page)
if flag:
print(page.url)
await page.keyboard.press('Enter')
await get_cookie(page)
else:
await page.keyboard.press('Enter')
await page.waitFor(20)
await page.waitForNavigation()
try:
global error
error = await page.Jeval('.error', 'node => node.textContent')
except Exception as e:
error = None
print(e, "错啦")
finally:
if error:
print('确保账户安全重新入输入')
else:
print(page.url)
# 可继续网页跳转 已经携带 cookie
# await get_search(page)
await get_cookie(page)
await page_close(browser)
async def page_close(browser):
for _page in await browser.pages():
await _page.close()
await browser.close()
async def get_search(page):
# https://s.taobao/search?q={查询的条件}&p4ppushleft=1%2C48&s={每页 44 条 第一页 0 第二页 44}&sort=sale-desc
await page.goto("https://s.taobao/search?q=气球")
await asyncio.sleep(5)
# print(await page.content())
# 获取登录后cookie
async def get_cookie(page):
res = await page.content()
cookies_list = await page.cookies()
cookies = ''
for cookie in cookies_list:
str_cookie = '{0}={1};'
str_cookie = str_cookie.format(cookie.get('name'), cookie.get('value'))
cookies += str_cookie
print(cookies)
# 将cookie 放入 cookie 池 以便多次请求 封账号 利用cookie 对搜索内容进行爬取
return cookies
if __name__ == '__main__':
tb_username = '淘宝用户名'
tb_pwd = '淘宝密码'
tb_url = "https://login.taobao/member/login.jhtml"
loop = asyncio.get_event_loop()
loop.run_until_complete(main(tb_username, tb_pwd, tb_url))
利用 上面 获取到的 cookie 爬取搜索内容
示例代码:
import json
import requests
import re
# 设置 cookie 池 随机发送请求 通过 pyppeteer 获取 cookie
cookie = '_tb_token_=edd7e354dee53;t=fed8f4ca1946ca1e73223cfae04bc589;sg=20f;cna=2uJSFdQGmDMCAbfFWXWAC4Jv;cookie2=1db6cd63ad358170ea13319f7a862c33;_l_g_=Ug%3D%3D;v=0;unb=3150916610;skt=49cbfd5e01d1b550;cookie1=BxVRmD3sh19TaAU6lH88bHw5oq%2BgcAGcRe229Hj5DTA%3D;csg=cf45a9e2;uc3=vt3=F8dByEazRMnQZDe%2F9qI%3D&id2=UNGTqfZ61Z3rsA%3D%3D&nk2=oicxO%2BHX4Pg%3D&lg2=U%2BGCWk%2F75gdr5Q%3D%3D;existShop=MTU1Njg3MDM3MA%3D%3D;tracknick=%5Cu7433150322;lgc=%5Cu7433150322;_cc_=V32FPkk%2Fhw%3D%3D;mt=ci=86_1;dnk=%5Cu7433150322;_nk_=%5Cu7433150322;cookie17=UNGTqfZ61Z3rsA%3D%3D;tg=0;enc=tThHs6Sn3BAl8v1fu3J4tMpgzA1n%2BLzxjib0vDAtGsXJCb4hqQZ7Z9fHIzsN0WghdcKEsoeKz6mBwPUpyzLOZw%3D%3D;JSESSIONID=B3F383B3467EC60F8CA425935232D395;l=bBMspAhrveV5732DBOCanurza77OSIRYYuPzaNbMi_5pm6T_G4QOlC03xF96VjfRswYBqh6Mygv9-etuZ;hng=CN%7Czh-CN%7CCNY%7C156;isg=BLi41Q8PENDal3xUVsA-aPbfiWaKiRzB6vcTu_IpBPOmDVj3mjHsO86vxUQYW9SD;uc1=cookie16=W5iHLLyFPlMGbLDwA%2BdvAGZqLg%3D%3D&cookie21=W5iHLLyFeYZ1WM9hVnmS&cookie15=UIHiLt3xD8xYTw%3D%3D&existShop=false&pas=0&cookie14=UoTZ4ttqLhxJww%3D%3D&tag=8&lng=zh_CN;thw=cn;x=e%3D1%26p%3D*%26s%3D0%26c%3D0%26f%3D0%26g%3D0%26t%3D0;swfstore=34617;'
headers = {
'cookie': cookie,
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
}
rep = requests.get('https://s.taobao/search?q=手机&p4ppushleft=1%2C48&s=0&sort=sale-desc ', headers=headers)
rep.encoding = 'utf-8'
res = rep.text
print(res)
r = repile(r'g_page_config = (.*?)g_srp_loadCss', re.S)
res = r.findall(res)
data = res[0].strip().rstrip(';')
dic_data = json.loads(data)
auctions = dic_data.get('mods')['itemlist']['data']['auctions']
# print(auctions,len(auctions))
for item in auctions[1:]:
print(item)
break
针对 iframe 的操作
- page.frames 获取所有的 iframe 列表 需要判断操作的是哪一个 iframe 跟操作 page 一样操作
from pyppeteer import launch
import asyncio
async def main(url):
w = await launch({'headless': False, 'args': ['--no-sandbox'], })
page = await w.newPage()
await page.setViewport({"width": 1366, 'height': 800})
await page.goto(url)
try:
await asyncio.sleep(1)
frame = page.frames
print(frame) # 需要找到是哪一个 frame
title = await frame[1].title()
print(title)
await asyncio.sleep(1)
login = await frame[1].querySelector('#switcher_plogin')
print(login)
await login.click()
await asyncio.sleep(20)
except Exception as e:
print(e, "EEEEEEEEE")
for _page in await w.pages():
await _page.close()
await w.close()
asyncio.get_event_loop().run_until_complete(main("https://i.qq/?rd=1"))
# asyncio.get_event_loop().run_until_complete(main("https://www.gushici/"))
与 scrapy 的整合
加入downloadmiddleware
from scrapy import signals
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import random
import pyppeteer
import asyncio
import os
from scrapy.http import HtmlResponse
pyppeteer.DEBUG = False
class FundscrapyDownloaderMiddleware(object):
# Not all methods need to be defined. If a method is not defined,
# scrapy acts as if the downloader middleware does not modify the
# passed objects.
def __init__(self) :
print("Init downloaderMiddleware use pypputeer.")
os.environ['PYPPETEER_CHROMIUM_REVISION'] ='588429'
# pyppeteer.DEBUG = False
print(os.environ.get('PYPPETEER_CHROMIUM_REVISION'))
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(self.getbrowser())
loop.run_until_complete(task)
#self.browser = task.result()
print(self.browser)
print(self.page)
# self.page = await browser.newPage()
async def getbrowser(self):
self.browser = await pyppeteer.launch()
self.page = await self.browser.newPage()
# return await pyppeteer.launch()
async def getnewpage(self):
return await self.browser.newPage()
@classmethod
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def process_request(self, request, spider):
# Called for each request that goes through the downloader
# middleware.
# Must either:
# - return None: continue processing this request
# - or return a Response object
# - or return a Request object
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(self.usePypuppeteer(request))
loop.run_until_complete(task)
# return task.result()
return HtmlResponse(url=request.url, body=task.result(), encoding="utf-8",request=request)
async def usePypuppeteer(self, request):
print(request.url)
# page = await self.browser.newPage()
await self.page.goto(request.url)
content = await self.page.content()
return content
def process_response(self, request, response, spider):
# Called with the response returned from the downloader.
# Must either;
# - return a Response object
# - return a Request object
# - or raise IgnoreRequest
return response
def process_exception(self, request, exception, spider):
# Called when a download handler or a process_request()
# (from other downloader middleware) raises an exception.
# Must either:
# - return None: continue processing this exception
# - return a Response object: stops process_exception() chain
# - return a Request object: stops process_exception() chain
pass
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
实战 异步爬取
示例 1 :快速上手
接下来我们测试下基本的页面渲染操作,这里我们选用的网址为:http://quotes.toscrape/js/,这个页面是 JavaScript 渲染而成的,用基本的 requests 库请求得到的 HTML 结果里面是不包含页面中所见的条目内容的。
为了证明 requests 无法完成正常的抓取,我们可以先用如下代码来测试一下:
import requests
from pyquery import PyQuery as pq
url = 'http://quotes.toscrape/js/'
response = requests.get(url=url)
doc = pq(response.text)
print('Quotes : {0}'.format(doc('.quote').length))
# 结果
# Quotes : 0
这里首先使用 requests 来请求网页内容,然后使用 pyquery 来解析页面中的每一个条目。观察源码之后我们发现每个条目的 class 名为 quote,所以这里选用了 .quote 这个 CSS 选择器来选择,最后输出条目数量。
运行结果:Quotes: 0
结果是 0,这就证明使用 requests 是无法正常抓取到相关数据的。
为什么?
因为这个页面是 JavaScript 渲染而成的,我们所看到的内容都是网页加载后又执行了 JavaScript 之后才呈现出来的,因此这些条目数据并不存在于原始 HTML 代码中,而 requests 仅仅抓取的是原始 HTML 代码。
好的,所以遇到这种类型的网站我们应该怎么办呢?
其实答案有很多:
- 分析网页源代码数据,如果数据是隐藏在 HTML 中的其他地方,以 JavaScript 变量的形式存在,直接提取就好了。
- 分析 Ajax,很多数据可能是经过 Ajax 请求时候获取的,所以可以分析其接口。
- 模拟 JavaScript 渲染过程,直接抓取渲染后的结果。
而 Pyppeteer 和 Selenium 就是用的第三种方法,下面我们再用 Pyppeteer 来试试,如果用 Pyppeteer 实现如上页面的抓取的话,代码就可以写为如下形式:
import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
async def main():
browser = await launch()
page = await browser.newPage()
url = 'http://quotes.toscrape/js/'
await page.goto(url=url)
doc = pq(await page.content())
print('Quotes : {0}'.format(doc('.quote').length))
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
运行结果:Quotes: 10
看运行结果,这说明我们就成功匹配出来了 class 为 quote 的条目,总数为 10 条,具体的内容可以进一步使用 pyquery 解析查看。
那么这里面的过程发生了什么?
实际上,Pyppeteer 整个流程就完成了浏览器的开启、新建页面、页面加载等操作。另外 Pyppeteer 里面进行了异步操作,所以需要配合 async/await 关键词来实现。首先, launch 方法会新建一个 Browser 对象,然后赋值给 browser,然后调用 newPage 方法相当于浏览器中新建了一个选项卡,同时新建了一个 Page 对象。然后 Page 对象调用了 goto 方法就相当于在浏览器中输入了这个 URL,浏览器跳转到了对应的页面进行加载,加载完成之后再调用 content 方法,返回当前浏览器页面的源代码。然后进一步地,我们用 pyquery 进行同样地解析,就可以得到 JavaScript 渲染的结果了。另外其他的一些方法如调用 asyncio 的 get_event_loop 等方法的相关操作则属于 Python 异步 async 相关的内容了,大家如果不熟悉可以了解下 Python 的 async/await 的相关知识。好,通过上面的代码,我们就可以完成 JavaScript 渲染页面的爬取了。
模拟网页截图,保存 PDF,执行自定义的 JavaScript 获得特定的内容
接下来我们再看看另外一个例子,这个例子可以模拟网页截图,保存 PDF,另外还可以执行自定义的 JavaScript 获得特定的内容,代码如下:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch()
page = await browser.newPage()
url = 'http://quotes.toscrape/js/'
await page.goto(url=url)
await page.screenshot(path='test_screenshot.png')
await page.pdf(path='test_pdf.pdf')
# 在网页上执行js 脚本
dimensions = await page.evaluate(pageFunction='''() => {
return {
width: document.documentElement.clientWidth, // 页面宽度
height: document.documentElement.clientHeight, // 页面高度
deviceScaleFactor: window.devicePixelRatio, // 像素比 1.0000000149011612
}
}''', force_expr=False) # force_expr=False 执行的是函数
print(dimensions)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
# 结果
# {'width': 800, 'height': 600, 'deviceScaleFactor': 1}
这里我们又用到了几个新的 API,完成了网页截图保存、网页导出 PDF 保存、执行 JavaScript 并返回对应数据。
evaluate 方法执行了一些 JavaScript,JavaScript 传入的是一个函数,使用 return 方法返回了网页的宽高、像素大小比率三个值,最后得到的是一个 JSON 格式的对象。
总之利用 Pyppeteer 我们可以控制浏览器执行几乎所有动作,想要的操作和功能基本都可以实现,用它来自由地控制爬虫当然就不在话下了。
了解了基本的实例之后,我们再来梳理一下 Pyppeteer 的一些基本和常用操作。Pyppeteer 的几乎所有功能都能在其官方文档的 API Reference 里面找到,链接为:API Reference — Pyppeteer 0.0.25 documentation,用到哪个方法就来这里查询就好了,参数不必死记硬背,即用即查就好。
登录淘宝 (打开网页后,手动输入用户名和密码,可以看到正常跳转到登录后的页面):
import asyncio
from pyppeteer import launch
width, height = 1366, 768
js1 = '''() =>{Object.defineProperties(navigator,{ webdriver:{ get: () => false}})}'''
js2 = '''() => {alert(window.navigator.webdriver)}'''
js3 = '''() => {window.navigator.chrome = {runtime: {}, }; }'''
js4 = '''() =>{Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});}'''
js5 = '''() =>{Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5,6],});}'''
async def page_evaluate(page):
# 替换淘宝在检测浏览时采集的一些参数
# 需要注意,在测试的过程中发现登陆成功后页面的该属性又会变成True
# 所以在每次重新加载页面后要重新设置该属性的值。
await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {}, }; }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
async def main():
browser = await launch(
headless=False,
# userDataDir='./userdata',
args=['--disable-infobars', f'--window-size={width},{height}', '--no-sandbox']
)
page = await browser.newPage()
await page.setViewport(
{
"width": width,
"height": height
}
)
url = 'https://www.taobao'
await page.goto(url=url)
# await page.evaluate(js1)
# await page.evaluate(js3)
# await page.evaluate(js4)
# await page.evaluate(js5)
await page_evaluate(page)
await asyncio.sleep(100)
# await browser.close()
asyncio.get_event_loop().run_until_complete(main())
如果把上面 js 去掉,发现淘宝可以检测出来, 跳转不到登录后的页面。
window.navigator 对象包含有关访问者浏览器的信息:https://www.runoob/js/js-window-navigator.html
js 主要需要修改浏览器的 window.navigator.webdriver、window.navigator.languages等值。
打开正常的浏览器可以看到:
window.navigator.webdriver的值为undefined,而通过pyppeteer控制打开的浏览器该值为True,当被检测到该值为True的时候,则滑动会一直失败,所以我们需要修改该属性。需要注意,在测试的过程中发现登陆成功后页面的该属性又会变成True,所以在每次重新加载页面后要重新设置该属性的值。
async def page_evaluate(page):
# 替换淘宝在检测浏览时采集的一些参数
await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {}, }; }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
另一种方法可以进一步免去淘宝登录的烦恼,那就是设置用户目录。
平时我们已经注意到,当我们登录淘宝之后,如果下次再次打开浏览器发现还是登录的状态。这是因为淘宝的一些关键 Cookies 已经保存到本地了,下次登录的时候可以直接读取并保持登录状态。
那么这些信息保存在哪里了呢?其实就是保存在用户目录下了,里面不仅包含了浏览器的基本配置信息,还有一些 Cache、Cookies 等各种信息都在里面,如果我们能在浏览器启动的时候读取这些信息,那么启动的时候就可以恢复一些历史记录甚至一些登录状态信息了。
这也就解决了一个问题:很多朋友在每次启动 Selenium 或 Pyppeteer 的时候总是是一个全新的浏览器,那就是没有设置用户目录,如果设置了它,每次打开就不再是一个全新的浏览器了,它可以恢复之前的历史记录,也可以恢复很多网站的登录信息。
当然可能时间太久了,Cookies 都过期了,那还是需要登录的。
那么这个怎么来做呢?很简单,在启动的时候设置 userDataDir 就好了,示例如下:
browser = await launch(
headless=False,
userDataDir='./userdata',
args=['--disable-infobars', f'--window-size={width},{height}']
)
用户文件夹
具体的介绍可以看官方的一些说明,如:https://chromium.googlesource/chromium/src/+/master/docs/user_data_dir.md 这里面介绍了 userdatadir 的相关内容。
命令行启动 chrome 并进入指定的 URL:chrome.exe --disable-infobars --user-data-dir="./userdatadir" --new-window https://login.taobao/member/login.jhtml
执行完后会打开 淘宝的登录页面,登录淘宝,然后保存用户名密码,这样登录信息就保存在 userdatadir 目录下了
在执行 chrome.exe --disable-infobars --user-data-dir="./userdatadir" --new-window https://www.taobao
可以看到已经时登录状态了。
爬取今日头条
# -*- coding: utf-8 -*-
# @Author :
# @File : toutiao.py
# @Software: PyCharm
# @description : XXX
import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
def screen_size():
"""使用tkinter获取屏幕大小"""
import tkinter
tk = tkinter.Tk()
width = tk.winfo_screenwidth()
height = tk.winfo_screenheight()
tk.quit()
return width, height
async def main():
width, height = screen_size()
print(f'screen : [ width:{width} , height:{height} ]')
browser = await launch(headless=False, args=[f'--window-size={width},{height}'])
page = await browser.newPage()
await page.setViewport({'width': width, 'height': height})
# 是否启用JS,enabled设为False,则无渲染效果
await page.setJavaScriptEnabled(enabled=True)
await page.goto('https://www.toutiao')
await asyncio.sleep(5)
print(await page.cookies()) # 打印页面cookies
print(await page.content()) # 打印页面文本
print(await page.title()) # 打印当前页标题
# 抓取新闻标题
title_elements = await page.xpath('//div[@class="title-box"]/a')
for item in title_elements:
# 获取文本
title_str = await (await item.getProperty('textContent')).jsonValue()
print(await item.getProperty('textContent'))
# 获取链接
title_link = await (await item.getProperty('href')).jsonValue()
print(title_str)
print(title_link)
# 在搜索框中输入python
await page.type('input.tt-input__inner', 'python')
# 点击搜索按钮
await page.click('button.tt-button')
await asyncio.sleep(5)
# print(page.url)
# 今日头条点击后新开一个页面, 通过打印url可以看出page还停留在原页面
# 以下用于切换至新页面
pages = await browser.pages()
page = pages[-1]
# print(page.url)
page_source = await page.content()
text = pq(page_source)
await page.goto(
url="https://www.toutiao/api/search/content/?"
"aid=24&app_name=web_search&offset=60&format=json"
"&keyword=python&autoload=true&count=20&en_qc=1"
"&cur_tab=1&from=search_tab&pd=synthesis×tamp=1555589585193"
)
for i in range(1, 10):
print(text("#J_section_{} > div > div > div.normal.rbox > div > div.title-box > a > span".format(i)).text())
# 关闭浏览器
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
4、selenium 反检测
利用 Chrome DevTools 协议
Chrome DevTools Protocol (协议详细内容):https://chromedevtools.github.io/devtools-protocol/
之前淘宝对于 selenium 还是很友好的,后来 selenium 被检测了 window.navigator.webdriver 等参数,出滑动验证码什么的,selenium 已经很难用了,
利用 Chrome DevTools 协议。它允许客户 检查 和 调试 Chrome 浏览器。
在 系统环境变量 PATH 里将 chrome的路径 添加进去。
打开cmd,在命令行中输入命令:chrome.exe --remote-debugging-port=9999 --user-data-dir="C:\selenum\AutomationProfile"
对于-remote-debugging-port 值,可以指定任何打开的端口。
对于-user-data-dir 标记,指定创建新 Chrome 配置文件的目录。它是为了确保在单独的配置文件中启动 chrome,不会污染你的默认配置文件。
执行完命令后,会打开一个浏览器页面,我们输入淘宝网址(https://login.taobao/member/login.jhtml),输入用户名和密码,登录淘宝后用户信息就保存在 --user-data-dir="C:\selenum\AutomationProfile" 所指定的文件夹中。
执行 js window.open() 打不开窗口时,是因为 chrome 默认不允许弹出窗口,改下 chrome 设置就可以了
在 chrome 浏览器地址栏输入:chrome://settings/content/popups,把 已阻止(推荐) 改成 允许 即可。
或者 chrome -》设置 -》高级 -》隐私设置和安全性 -》网站设置 -》弹出式窗口和重定向,也可以设置。
不要关闭上面浏览器,然后执行 python 代码。在淘宝搜索 "电脑" 关键字,并打印前 5 页 所有 搜索内容
import os
import time
import random
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdrivermon.by import By
from selenium.webdriver.support import expected_conditions as EC
# from selenium.webdrivermon.action_chains import ActionChains
def main():
# os.system(r'C:\Users\Administrator\AppData\Local\Google\Chrome\Application/chrome.exe --remote-debugging-port=9999 --user-data-dir="C:\selenum\AutomationProfile"')
chrome_debug_port = 9999
chrome_options = Options()
# chrome_options.add_argument('--headless')
chrome_options.add_experimental_option("debuggerAddress", f"127.0.0.1:{chrome_debug_port}")
browser = webdriver.Chrome(chrome_options=chrome_options)
wait = WebDriverWait(browser, 5)
print(browser.title)
# 当前句柄
current_handle = browser.current_window_handle
# browser.execute_script('window.open("https://login.taobao/member/login.jhtml")')
browser.execute_script('window.open("http://www.baidu")')
# 所有句柄
all_handle = browser.window_handles
second_handle = all_handle[-1]
# 切回first
browser.switch_to.window(current_handle)
url = 'https://s.taobao/search?q=电脑'
browser.get(url)
produce_info_xpath = '//div[contains(@class, "J_MouserOnverReq")]//div[@class="row row-2 title"]/a'
produce_info = browser.find_elements_by_xpath(produce_info_xpath)
for produce in produce_info:
print(produce.text.replace(' ', ''))
# 这里是演示,所以只爬了前 5 页
for page_num in range(2, 6):
next_page_xpath = '//li[@class="item next"]'
next_page = browser.find_element_by_xpath(next_page_xpath)
next_page_enable = False if 'disabled' in next_page.get_attribute('class') else True
if next_page_enable:
print('*' * 100)
print(f'第 {page_num} 页')
next_page.click()
# browser.refresh()
produce_info_xpath = '//div[contains(@class, "J_MouserOnverReq")]//div[@class="row row-2 title"]/a'
wait.until(EC.presence_of_all_elements_located((By.XPATH, produce_info_xpath)))
time.sleep(random.randint(3, 5))
produce_info = browser.find_elements_by_xpath(produce_info_xpath)
for produce in produce_info:
print(produce.text.replace(' ', ''))
else:
break
if __name__ == '__main__':
main()
代码 2(根据关键字搜索,然后抓取 店铺名,店铺地址,店铺电话,):
# -*- coding: utf-8 -*-
import time
import random
import parsel
import re
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdrivermon.by import By
from selenium.webdriver.support import expected_conditions as EC
# from selenium.webdrivermon.action_chains import ActionChains
class TaoBaoSearch(object):
def __init__(self):
super(TaoBaoSearch, self).__init__()
self.browser = None
self.wait = None
self.master_handler = None
self.slaver_handler = None
self.temp = None
self.browser_init()
def browser_init(self):
chrome_debug_port = 9999
chrome_options = Options()
chrome_options.add_experimental_option("debuggerAddress", f"127.0.0.1:{chrome_debug_port}")
# chrome_options.add_argument('--headless')
self.browser = webdriver.Chrome(chrome_options=chrome_options)
self.wait = WebDriverWait(self.browser, 5)
all_handler = self.browser.window_handles
if len(all_handler) >= 1:
for index in all_handler[1:]:
self.browser.switch_to.window(index)
self.browser.close()
# self.master_handler = self.browser.current_window_handle
self.master_handler = self.browser.window_handles[0]
self.browser.switch_to.window(self.master_handler)
self.browser.execute_script('window.open()')
# self.browser.execute_script('window.open("_blank")')
handlers = self.browser.window_handles
self.slaver_handler = handlers[-1]
# print(self.browser.title)
def get_detail_info(self, shop_url=None):
# 切换到 从 窗口
self.browser.switch_to.window(self.slaver_handler)
self.browser.get(shop_url)
html = self.browser.page_source
html = html.replace('<', '<').replace('>', '>')
# print(html)
s_html = parsel.Selector(text=html)
shop_keeper_xpath = '//div[@class="extend"]//li[@class="shopkeeper"]//a/text()'
shop_keeper = s_html.xpath(shop_keeper_xpath).extract_first()
phone_reg = '联系电话:(\d+-?\d+)|联系手机:(\d+)'
phone = re.findall(phone_reg, html)
# 处理完后 一定要切换到 主 窗口
self.browser.switch_to.window(self.master_handler)
return shop_keeper, phone
def process_item(self, item):
self.temp = None
shop_xpath = './/div[@class="shop"]//a'
local_xpath = './/div[@class="location"]'
shop = item.find_element_by_xpath(shop_xpath).text
shop_url = item.find_element_by_xpath(shop_xpath).get_attribute('href')
local = item.find_element_by_xpath(local_xpath).text
shop_keeper, phone = self.get_detail_info(shop_url)
if phone:
print(f'shop : {shop}')
print(f'local : {local}')
print(f'shop_url : {shop_url}')
print(f'shop_keeper : {shop_keeper}')
print(f'phone : {phone}')
with open('./info.txt', 'a+') as f:
f.write(shop + ',')
f.write(local + ',')
f.write(shop_url + ',')
f.write(shop_keeper + ',')
f.write(f'{phone}')
f.write('\n')
def main(self):
# 切回 主 窗口
self.browser.switch_to.window(self.master_handler)
key_word = input('输入淘宝搜索关键字:')
if not key_word:
print('没有输入关键字。默认搜索 “手机”')
key_word = '手机'
url = f'https://s.taobao/search?q={key_word}'
self.browser.get(url)
shop_and_local_xpath = '//div[contains(@class, "J_MouserOnverReq")]//div[@class="row row-3 g-clearfix"]'
shop_and_local = self.browser.find_elements_by_xpath(shop_and_local_xpath)
for item in shop_and_local:
self.process_item(item)
# 这里是演示,所以只爬了前 5 页
for page_num in range(2, 6):
next_page_xpath = '//li[@class="item next"]'
next_page = self.browser.find_element_by_xpath(next_page_xpath)
next_page_enable = False if 'disabled' in next_page.get_attribute('class') else True
if next_page_enable:
print('*' * 100)
print(f'第 {page_num} 页')
next_page.click()
# self.browser.refresh()
self.wait.until(EC.presence_of_all_elements_located((By.XPATH, shop_and_local_xpath)))
time.sleep(random.randint(3, 5))
shop_and_local = self.browser.find_elements_by_xpath(shop_and_local_xpath)
for item in shop_and_local:
self.process_item(item)
else:
break
if __name__ == '__main__':
tb = TaoBaoSearch()
tb.main()
headless 模式
上面是一直有浏览器窗口的,没法使用 无头模式,可以使用 --user-data-dir 参数,然后设置无头模式。如果想改变 Chrome 位置,可以设置 chrome_options.binary_location 为 chrome.exe 路径即可。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
if __name__ == '__main__':
chrome_options = Options()
# 不使用默认的Chrome安装版本时,可以设置binary_location 指定 Chrome 路径 。
# chrome 和 Chromium 对应 chromedriver.exe 版本不一样
chrome_options.binary_location = r'D:\chrome\chrome.exe'
# chrome_options.binary_location = r'D:\Chromium\chrome.exe'
# chrome_options.add_argument('--headless')
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument('disable-infobars')
chrome_options.add_argument(r'--user-data-dir=D:\chrome\userdatadir')
# chrome_options.add_argument(r'--user-data-dir=D:\Chromium\userdatadir')
browser = webdriver.Chrome(
chrome_options=chrome_options,
executable_path=r'D:\chrome\chromedriver.exe'
# executable_path=r'D:\Chromium\chromedriver.exe'
)
browser.get('https://www.taobao/')
user_name_xpath = '//div[@class="site-nav-user"]/a'
user_name = browser.find_element_by_xpath(user_name_xpath).text
print(user_name)
可以看到 无头模式下,使用 --user-data-dir 参数,可以登录淘宝。前提需要先手动登录淘宝,拿到登录信息的文件夹。
ichrome
github 地址:https://github/ClericPy/ichrome
这里就不放天猫、淘宝的代码了,贴一个药监局的:
( 流程:药品 ---> 药品查询 ---> 国产药品 ,然后就一直翻页)
import asyncio
from lxml import etree
from ichrome import AsyncChromeDaemon
async def main():
async with AsyncChromeDaemon(headless=0, disable_image=False) as cd:
async with cd.connect_tab(index=0, auto_close=True) as tab:
url = 'https://www.nmpa.gov/yaopin/index.html'
wait_timeout = 5
await tab.goto(url, timeout=wait_timeout)
await asyncio.sleep(2)
data_query_css_string = '#layer3 > div > a:nth-child(9)'
await tab.wait_tag(data_query_css_string, max_wait_time=wait_timeout)
await tab.click(data_query_css_string, timeout=wait_timeout)
await asyncio.sleep(2)
yao_query_css_string = '[title="国家局批准的药品批准文号信息"]'
await tab.wait_tag(yao_query_css_string, max_wait_time=wait_timeout)
await tab.click(yao_query_css_string, timeout=wait_timeout)
await asyncio.sleep(2)
while True:
data_link_css_string = '#content table:nth-child(2) > tbody > tr:nth-child(1) > td > p > a'
await tab.wait_tag(data_link_css_string, timeout=wait_timeout)
html = await tab.get_html(timeout=wait_timeout)
s_html = etree.HTML(text=html)
s_table = s_html.xpath('//div[@id="content"]//table')[2]
s_tr_list = s_table.xpath('.//tr')
for s_tr in s_tr_list:
tag_a = s_tr.xpath('string(.)').strip()
print(tag_a)
# tag_a_href = s_tr.xpath('.//a/@href')
# print(tag_a_href)
btn_next = '[src="images/dataanniu_07.gif"]'
await tab.click(btn_next, timeout=wait_timeout)
await asyncio.sleep(2)
if __name__ == "__main__":
asyncio.run(main())
chrome 多开:设置不同的 debug_port 和 user_data_dir 可以达到多开 Chrome
import json
import asyncio
import aiomultiprocess
from loguru import logger
from ichrome import AsyncChromeDaemon
from ichrome.async_utils import Chrome
async def startup_chrome(dp_port=None):
"""
设置 chrome 参数,然后启动 chrome
:param dp_port: 自定义 debug port
:return:
"""
logger.info(f'dp_port ---> {dp_port}')
timeout = 5
# 也可以给 Chrome 添加代理
proxy = '127.0.0.1:8080'
udd= f'c:/chrome_user_data_dir_{dp_port}'
async with AsyncChromeDaemon(port=dp_port, proxy=proxy, user_data_dir=udd) as cd:
async with cd.connect_tab(index=0) as tab:
url = 'https://space.bilibili/1904149/'
await tab.set_url(url, timeout=timeout)
await asyncio.sleep(5)
cookie = await tab.get_cookies(url, timeout=timeout)
cookie_string = json.dumps(cookie, ensure_ascii=False)
logger.info(f'cookie_string ---> {cookie_string}')
async def main():
db_list = [9301 + offset for offset in range(5)]
async with aiomultiprocess.Pool() as aio_pool:
await aio_pool.map(startup_chrome, db_list)
await aio_pool.join()
if __name__ == "__main__":
asyncio.run(main())
pass
undetected_chromedrive
github:https://github/search?q=undetected-chromedriver
github 搜索 selenium,看看还有没有其他的
pip install git+https://github/ultrafunkamsterdam/undetected-chromedriver.git
简单的例子 ,懂车帝对selenium反爬挺厉害,通过undetected_chromedriver可轻松搞定。
import undetected_chromedriver as uc
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
uc.TARGET_VERSION = 91
driver = uc.Chrome()
driver.get('https://www.dongchedi/user/53334173333')
driver.find_element_by_xpath('//*[@id="__next"]/div[1]/div[2]/div/div[1]/div[2]/header/nav/a[2]').click()
driver.close()
5、Playwright
Playwright 官网文档
github 搜索:https://github/search?q=Playwright
Playwright
- :https://github/microsoft/playwright
- :https://github/microsoft/playwright-python
scrapy-playwright:https://github/scrapy-plugins/scrapy-playwright
官网文档 ( docs、API ):https://playwright.dev/python/docs/intro
特点
- 支持所有浏览器。Playwright 可以在所有浏览器中实现快速、可靠和强大的自动化测
- 快速可靠的执行
- 强大的自动化功能
- 与你的工作流集成
为什么选择 Playwright
:https://wwwblogs/fnng/p/14274960.html
Python 示例代码
基于 playwright 和 pytest 单元测试框架的自动化项目
:https://github/defnngj/playwright-pro
同步 代码 示例
from playwright.sync_api import sync_playwright
def run(playwright):
firefox = playwright.firefox
browser = firefox.launch()
page = browser.new_page()
page.goto("https://example")
browser.close()
with sync_playwright() as playwright:
run(playwright)
playwright 支持创建多个Browser contexts,相当于是打开浏览器后,可以创建多个页面上下文,每个上下文做的操作可以不同
from playwright.sync_api import sync_playwright
# 打开两个浏览器上下文
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=100) # 打开浏览器
context1 = browser.new_context() # 创建浏览器上下文,支持创建多个上下文
page1 = context1.new_page() # 新打开一个浏览器标签页
page1.goto("https://www.baidu")
context2 = browser.new_context() # 创建浏览器上下文,支持创建多个上下文
page2 = context2.new_page() # 新打开一个浏览器标签页
page2.goto("https://www.bilibili")
browser.close()
运行效果如图。打开两个浏览器实例,
一个浏览器上下文就相当于一个 浏览器 实例,浏览器和上下文都可以使用 new_page() 方法打开一个新的浏览器标签页(选项卡)
browser = p.chromium.launch(headless=False)
page = browser.new_page()
当我们通过点击某些按钮/超链接打开一个新的浏览器标签页时,还需要继续在这个浏览器标签页上继续操作时,那么可以使用以下方式
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=100) # 打开浏览器
context1 = browser.new_context() # 创建浏览器上下文,支持创建多个上下文
page1 = context1.new_page()
page1.goto("https://www.baidu")
with context1.expect_page() as new_page_info:
page1.click('//a[contains(@href, "https://www.hao123")]') # 在百度首页点击hao123后会打开一个新的选项卡
new_page = new_page_info.value
new_page.click('//a[contains(text(), "hao123推荐")]') # 在hao123点击hao123推荐
pass
参考:https://playwright.dev/python/docs/multi-pages
异步 代码 示例
import asyncio
from playwright.async_api import async_playwright
async def run(playwright):
firefox = playwright.firefox
browser = await firefox.launch()
page = await browser.new_page()
await page.goto("https://example")
await browser.close()
async def main():
async with async_playwright() as playwright:
await run(playwright)
asyncio.run(main())
新兴爬虫利器 Playwright 的基本用法
:https://cuiqingcai/36045.html
playwright 教程
:https://blog.csdn/m0_51156601/article/details/126886040
更多推荐
爬虫教程( 5 ) --- Selenium、PhantomJS、selenium反检测、Playwright、Playwright-python、scrapy
发布评论