selenium_docker使用
Selenium Dokcer
我为了使用selenium,但是本地Chrome的版本太高了,Driver驱动不了。
启动命令
docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" seleniarm/standalone-chromium:latest
4444端口是控制端口,python调用这个端口
7900是监控端口,通过vnc,没有特殊指示的话,密码是secret
python调用远程selenium
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
SELENIUM_REMOTE_URL = "http://localhost:4444/wd/hub"
options = uc.ChromeOptions() # 使用uc来移除一些可能标识自己是爬虫的chrome配置
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_argument('--disable-infobars') # 移除受控的条
driver = webdriver.Remote(
command_executor=SELENIUM_REMOTE_URL,
options=options
)
selenium 走代理
这个代码我是确定正常成功的,本质上创建了一个插件,然后通过插件来走的代理,如果不需要账号密码,就移除authCredentials这个函数即可
def create_proxyauth_extension(proxy_host, proxy_port, proxy_username, proxy_password, scheme='http', plugin_path=None):
"""代理认证插件
args:
proxy_host (str): 你的代理地址或者域名(str类型)
proxy_port (int): 代理端口号(int类型)
# 用户名密码认证(私密代理/独享代理)
proxy_username (str):用户名(字符串)
proxy_password (str): 密码 (字符串)
kwargs:
scheme (str): 代理方式 默认http
plugin_path (str): 扩展的绝对路径
return str -> plugin_path
"""
if plugin_path is None:
plugin_path = 'vimm_chrome_proxyauth_plugin.zip'
manifest_json = """
{
"version": "1.0.0",
"manifest_version": 2,
"name": "Chrome Proxy",
"permissions": [
"proxy",
"tabs",
"unlimitedStorage",
"storage",
"<all_urls>",
"webRequest",
"webRequestBlocking"
],
"background": {
"scripts": ["background.js"]
},
"minimum_chrome_version":"22.0.0"
}
"""
background_js = string.Template(
"""
var config = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "${scheme}",
host: "${host}",
port: parseInt(${port})
},
bypassList: ["foobar.com"]
}
};
chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});
function callbackFn(details) {
return {
authCredentials: {
username: "${username}",
password: "${password}"
}
};
}
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);
"""
).substitute(
host=proxy_host,
port=proxy_port,
username=proxy_username,
password=proxy_password,
scheme=scheme,
)
with zipfile.ZipFile(plugin_path, 'w') as zp:
zp.writestr("manifest.json", manifest_json)
zp.writestr("background.js", background_js)
return plugin_path
proxy_auth_plugin_path = create_proxyauth_extension(
proxy_host="localhost",
proxy_port=7890,
proxy_username="123",
proxy_password="123"
)
SELENIUM_REMOTE_URL = "http://192.168.123.239:4445/wd/hub"
options = uc.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_argument('--disable-infobars')
options.add_extension(proxy_auth_plugin_path)
# options.add_argument('--proxy-server=socks5://localhost:7890') # 这个方式也可以,不过没法认证账号密码
driver = webdriver.Remote(
command_executor=SELENIUM_REMOTE_URL,
options=options
)
使用driver获取网页上的一些元素
driver.get("https://weread.qq.com/#search") # 打开网页
search_button = driver.find_element(By.CLASS_NAME, "search_input_text") # 找到有这个classname的元素,find_element会取第一个element,find_elements会返回一个列表,如果find_element没有找到会报错,find_elements不会,会返回空列表
search_button.send_keys("hello world") # 将文本提交到上面的输入框当中
driver.execute_script('document.getElementsByClassName("search_input_right")[0].click()') # 执行js脚本,这个是使用js模拟了点击,也可以使用driver.find_element(By.CLASS_NAME, "search_input_right").click()
last_height = driver.execute_script('return document.getElementsByClassName("search_result_global")[0].scrollHeight') # 如果需要某个js执行的返回值,就加入return,数据类型也会对应的转换,数字给数字,字符串给字符串
wait = WebDriverWait(driver, 10) # wait函数,会等待,知道返回了结果
ul_element = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'ul.search_result_global_list')))
li_elements = ul_element.find_elements(By.CSS_SELECTOR, 'li.search_result_global_item')
for index, li in enumerate(li_elements):
# 根据页面结构获取标题、作者、简介、URL
title = li.find_element(By.CSS_SELECTOR, 'p.search_result_global_bookTitle').text
author = li.find_element(By.CSS_SELECTOR, 'p.search_result_global_bookAuthor').text
description = li.find_element(By.CSS_SELECTOR, 'p.search_result_global_bookContent').text
url = li.find_element(By.CSS_SELECTOR, 'a').get_attribute('href') # 获取href属性
print(title, author, url)
# 正如前文所说的,find_elements来判断是否存在某个属性是更加合适的。至于text_content的文本内容为什么要这么获取,因为selenium只能获取当前元素的文本,即使子元素有文本,可是就算我遍历了却还是获取不到,所以就改成了使用js代码来获取
title_list = []
for i, item2 in enumerate(li_elements):
text_content = driver.execute_script(f'return document.getElementsByClassName("readerCatalog_list")[0].getElementsByTagName("li")[{i}].textContent')
if item2.find_elements(By.CSS_SELECTOR, ".readerCatalog_list_item_level_1"):
title_list.append("level1: " + text_content)
elif item2.find_elements(By.CSS_SELECTOR, ".readerCatalog_list_item_level_2"):
title_list.append("level2: " + text_content)
elif item2.find_elements(By.CSS_SELECTOR, ".readerCatalog_list_item_level_3"):
title_list.append("level3: " + text_content)
elif item2.find_elements(By.CSS_SELECTOR, ".readerCatalog_list_item_level_4"):
title_list.append("level4: " + text_content)
elif item2.find_elements(By.CSS_SELECTOR, ".readerCatalog_list_item_level_5"):
title_list.append("level5: " + text_content)
elif item2.find_elements(By.CSS_SELECTOR, ".readerCatalog_list_item_level_6"):
title_list.append("level6: " + text_content)
else:
print("error", read_url)
html_source = driver.page_source # 获取当前的html文本
如果要爬取很多内容,可以将爬取逻辑包裹在这个里面,核心逻辑是在try的最后面quit,报错了也quit。失败的内容就在跑完一批内容之后再重新跑
while 1:
if ...:
break
driver=...
try:
...
driver.quit()
except Exception as e:
driver.quit()
破解google recaptcha
使用一个插件即可。CAPTCHA Solver: auto hCAPTCHA reCAPTCHA freely 获取这个插件的crx文件,然后在启动的加载进去,这个插件会自动处理验证码。
如何获取这个crx呢?可以用这个插件Get CRX ,装了这个插件,再打开上面的链接,然后点击Get CRX插件就可以下载到上面的插件。
注意事项
使用远程的selenium,当需要更新driver的时候,一定要将上一个driver关掉,driver.quit() 不然无法开启下一次的网址打开。
Refrence
解决selenium爬虫加密代理问题(含socks5等一切代理)
获取子节点文本,但是不知道为什么不生效,还是使用上面的js的方式获取吧