经常会在一些爬虫群里面看到这样的提问,为什么用Python爬虫请求某个网页时,有时打印的数据不全或者什么数据都没有或者只有html骨架代码。这是因为涉及到了”动态网页数据“这个词了,简单而言,就是后台的数据不是请求网页链接时就已经将数据写入到相应的标签上了,而是利用ajax请求将后台的数据写入到相应的标签上。通常要得到这些数据,可以有两种方式,其一为找到这个ajax请求链接,然后访问这个链接,解析相应的json数据即可;另外一种是使用selenium访问这个网址,等待网页加载完之后,然后解析相应的html标签得到这些数据。
今天我们就来讲解下直接使用selenium模块访问当前网址,因为通过selenium访问网址时,是完全模拟浏览器进行访问的,因此,即使网页使用了ajax技术,selenium也能获取到相应的数据。selenium实现了一些类似xpath的功能,可以用driver直接获取我们想要的元素,直接调用下列方法,用pyquery方法解析的,相对要简单很多。
def get_products(): wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist .items .item'))) html = driver.page_source doc = pq(html) items = doc('#mainsrp-itemlist .items .item').items() for item in items: product = { 'image': item.find('.pic .img').attr('src'), 'price': item.find('.price').text(), 'deal': item.find('.deal-cnt').text()[:-3], 'title': item.find('.title').text(), 'shop': item.find('.shop').text(), 'location': item.find('.location').text() } print(product)
selenium还包括很多方法,在访问一些需要登陆的网站的时候我们可以使用selenium驱动浏览器进行操作。但是使用的过程中需要注意几点:
1、在开始爬取过程前,需要明确爬取目标和目标数据的结构。
2、使用合适的浏览器驱动:selenium需要一个浏览器驱动来控制浏览器,需要根据自己使用的浏览器版本下载相应版本的浏览器驱动。
3、设置合适的间隔时间:避免爬取过快导致封IP或者被识别为恶意爬虫,需要设置合适的间隔时间。
5、处理网页加载时的动态内容:对于需要模拟点击、滚动等动作才能显示出的网页内容,需要使用selenium提供的模拟点击、滚动等方法。
以下就是selenium加上Chrome版本>=92,并附带添加亿牛云代理IP的实现过程。
from selenium import webdriver import string import zipfile # 代理服务器(产品官网 www.16yun.cn) proxyHost = "t.16yun.cn" proxyPort = "3111" # 代理验证信息 proxyUser = "username" proxyPass = "password" def create_proxy_auth_extension(proxy_host, proxy_port, proxy_username, proxy_password, scheme='http', plugin_path=None): if plugin_path is None: plugin_path = r'/tmp/{}_{}@t.16yun.zip'.format(proxy_username, proxy_password) manifest_json = """ { "version": "1.0.0", "manifest_version": 2, "name": "16YUN 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: ["localhost"] } }; 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, ) print(background_js) 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_proxy_auth_extension( proxy_host=proxyHost, proxy_port=proxyPort, proxy_username=proxyUser, proxy_password=proxyPass) option = webdriver.ChromeOptions() option.add_argument("--start-maximized") # 如报错 chrome-extensions # option.add_argument("--disable-extensions") option.add_extension(proxy_auth_plugin_path) # 关闭webdriver的一些标志 # option.add_experimental_option('excludeSwitches', ['enable-automation']) driver = webdriver.Chrome( chrome_options=option, executable_path="./chromdriver" ) # 修改webdriver get属性 # script = ''' # Object.defineProperty(navigator, 'webdriver', { # get: () => undefined # }) # ''' # driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": script}) driver.get("https://httpbin.org/ip")