爬虫随笔(4):Python+Selenium+Chrome——三剑合璧

学习心得

从五月份到现在,学爬虫三个月了,也算对爬虫比较了解了。到目前为止,我最喜欢的还是Scrapy和模拟浏览器,虽然大家总是说模拟浏览器太慢,不过当你的爬虫对速度要求没那么高、对爬虫质量有要求、网站很复杂需要各种动态各种加载各种请求的时候,模拟浏览器就是最好(玩)的选择。

Python+Selenium+Chrome就是我现在用的主流模拟浏览器方案,模拟浏览器提供了非常多的接口方法和属性以供操作,目前还是可以满足我的需求的。后面会考虑把最近做的微信公众号爬虫放上来讲解,其中验证码破解时,模拟浏览器就帮了大忙。

文末附有这阵子写的一些代码,完全可用,欢迎参考或提出更好的方法!

准备

  • Pyhton3.6
  • pip install webdrive
  • 对应版本的浏览器驱动,例如我用的是Chrome,就去官网上下载和自己机器上对应版本的驱动,然后将驱动放在环境变量中,这个网上有很多教程了,不是要讲的重点。
  • PhantomJS是一个内置的小型无头浏览器内核(所谓无头就是运行时你看不到的),Python3.6目前使用会有警告,而且下载得去外网或者用anaconda,笔者一般不用。

代码分析

下面是我目前使用的模拟浏览器调用接口模块。

# -*- coding: utf-8 -*-
#   This module is used to get a selenium browser,
#   you can chose Firefox,Chrome,PhantomJS(em,maybe it's dead)
#   you can also chose how to config img,head,ua,ip.

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import random


USER_AGENTS = [
        'Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1',
        'Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070803 Firefox/1.5.0.12',
        'Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en) Opera 8.0',
        'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/20080219 Firefox/2.0.0.12 Navigator/9.0.0.6',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Maxthon/4.0.6.2000 Chrome/26.0.1410.43 Safari/537.1 ',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0 ',
        'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0; BIDUBrowser 2.x)',
        'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3; rv:11.0) like Gecko'
]
ips = []


def phantomjs_simu(image=False, ua=True, ip=False):
    dcap = dict(DesiredCapabilities.PHANTOMJS)
    if not image:
        dcap["phantomjs.page.settings.loadImages"] = False  # 禁止加载图片
    if ua:
        dcap["phantomjs.page.settings.userAgent"] = (random.choice(USER_AGENTS)) # 使用ua代理
    if ip:
        # 使用ip代理,未完善
        pass
    # 将配置信息生成的字典作为输入生成浏览器接口
    js_drive = webdriver.PhantomJS(desired_capabilities=dcap)
    return js_drive


def chrome_simu(head=False, image=False, ua=True, ip=False):
    options = webdriver.ChromeOptions()
    if not image:
        prefs = {
                'profile.default_content_setting_values': {
                        'images': 2
                        }
                }  # 禁止图片加载
        options.add_experimental_option('prefs', prefs)
    if not head:
        # 配置为无头模式
        options.add_argument('headless')
    if ua:
        options.add_argument('user-agent="%s"' % random.choice(USER_AGENTS))
    if ip:
        # ip代理,没有优质的代理所以一般没加
        proxy = random.choice(ips)
        options.add_argument('--proxy-server=%s' % proxy['ip'])
    options.add_argument('lang=zh_CN.UTF-8')

    ch_drive = webdriver.Chrome(chrome_options=options)
    return ch_drive


def firefox_simu(head=False, image=False, ua=True, ip=False):
    options = webdriver.FirefoxOptions()
    if not head:
        options.add_argument('-headless')  # 无头模式
    # 火狐没怎么用,所以配置也没完善,有需要的可以参照Chrome的自行补充
    if not image:
        pass
    if ua:
        pass
    if ip:
        pass
    ff_drive = webdriver.Firefox(firefox_options=options)
    return ff_drive


if __name__ == '__main__':
    # 调用后就相当于获得了一个浏览器的控制权,可以用程序对浏览器进行控制
    browser = chrome_simu(head=True, image=False, ua=True)

    # 简单的使用示例
    browser.get('http://www.baidu.com')
    print(browser.current_url)
    browser.close()
说说为什么我要设置上面4个配置?
  • 首先ua代理和ip代理都不用说,反爬基本意识;
  • 至于head这个参数可以选择浏览器是显式运行还是隐式运行,这对调试很有意义,你可以用显式调试代码,无误后在最终的工程中改为隐式,有利于运行速度;
  • image也好理解,就是选择图片加不加载,不加载可以提高运行速度,但在有验证码的页面且验证码图片的URL是动态的时候,就必须要显示图片,因为此时图片URL已经不能唯一确定一张图片了。

浏览器操作

下面记录几个webdrive用的比较频繁的属性和方法,以及几个实用的小技巧!

# attribute
browser.current_url # 当前浏览器的访问网址
browser.page_source # 页面的源代码
browser.window_handles # 当前浏览器的窗口控制数组
browser.current_window_handle # 当前窗口的控制

# method
browser.get(url) # 访问网站
browser.close() # 关闭网页
browser.quit() # 关闭浏览器
browser.back() # 网页后退
browser.set_page_load_timeout(10) # 设置页面加载超时
browser.set_script_timeout(10) # 设置脚本加载超时
browser.find_element_by_xpath('//div/../..') # 定位页面元素
browser.switch_to_window(browser.window_handles[i]) # 切换标签页
browser.execute_script('return ...') # 执行脚本

# tips
# 1.输入和点击提交
# 检查元素找到搜索框和提交键的xpath,可以拿百度试试
browser.find_element_by_xpath(xpath_input).send_keys(key_word)
time.sleep(2) # 建议不要瞬间输入瞬间搜索
browser.find_element_by_xpath(xpath_post).click()

# 2.定位并截图
# 当遇到动态验证码,想要把图片拿出来不能通过加载图片URL,因为再访问一次验证码已经变了,只能从页面截图
# 定位图片在网页上的位置和大小
img_element = id_drive.find_element_by_xpath('//div/../img')
img_location = img_element.location
img_size = img_element.size
rangle = (
    int(img_location['x']), int(img_location['y']),
    int(img_location['x'] + img_size['width']),
    int(img_location['y'] + img_size['height'])
    )
# 截图后根据图片在页面中的数据进行剪切
id_drive.save_screenshot('test.png')
image = Image.open('test.png').crop(rangle)
image.save('test2.png')

# 3.切换窗口
# 当有弹出新窗口的时候,就得注意浏览器操作是对哪个页面操作的问题了
handles = browser.window_handles
# 切换到新标签页进行操作
browser.switch_to_window(handles[1])

# 4.拉到底端
# 这是之前爬腾讯网时写的模拟鼠标下滑动作的方法,因为腾讯的页面都是下拉加载QAQ
# 下面代码的思路就是判断页面长度是否稳定了来判断是否完全加载
def RefreshScreen(browser):
    js1 = 'return document.body.scrollHeight'
    js2 = 'window.scrollTo(0, document.body.scrollHeight)'
    js3 = 'window.scrollTo(0, document.body.scrollHeight/4)'
    old_scroll_height = 0
    new_scroll_height = 0
    try_time = 0
    load_time = 0
    start = time.time()
    init = True
    while new_scroll_height >= old_scroll_height and try_time < 5:
        print("new scroll_height: %d" % browser.execute_script(js1))
        print("old scroll_height: %d" % old_scroll_height)
        if new_scroll_height == old_scroll_height:
            try_time += 1
        else:
            try_time = 0
            load_time += 1
            old_scroll_height = new_scroll_height
        print("No change of scroll_height: %d" % try_time)
        if init:
            browser.execute_script(js3)
            init = False
        browser.execute_script(js2)
        print("loading new data...\n")
        time.sleep(1)
        new_scroll_height = browser.execute_script(js1)
    print("No more data!")
    print("Loading times: %d" % load_time)
    print("Loading time: %d" % (time.time()-start))
    print("Loading length: %d" % new_scroll_height)

# 5.动态加载
# 有的时候还有这种现象,明明一些标签你在检查元素的时候检查到了,但当你获取页面源码里面却没有。
# 这是因为,有些页面的数据是动态加载出来的,像我这种没学过JS的怎么办?
# 使用模拟浏览器就可以跳过各种构造请求头的狗血操作,直接执行脚本来获取!
real_html = browser.execute_script('return document.documentElement.outerHTML')

目前在模拟浏览器这一块主要就用了这么些操作,后续再补充。

代码都是这阵子学了写了用在工程里的,环境Python3.6/Ubuntu16.04,欢迎参考,如有纰漏,评论区求怼。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值