zoukankan      html  css  js  c++  java
  • Python爬虫学习笔记7:动态渲染页面爬取

    参考:Python3网络爬虫开发实战

    问题:Ajax 是javascript动态渲染页面的一种情形,可以通过分析Ajax,然后借用requests和urllib来实现数据爬取。不过Javascript动态渲染的页面不止这一种。

    比如中国青年网(详见 http://news.youth.cn/gn/), 它的分页部分是由 JavaScript生成的,并非原始 HTML 代码,这其中并不包含 Ajax请求 。 比如 ECharts 的官方实例(详见 http://echarts.baidu.com/demo.html#bar-negative ),其图形都是经过 JavaScript计算之 后生成的。 再有淘宝这种页面,它即使是 Ajax获取的数据,但是其 Ajax接口含有很多加密参数,我 一一 们难以直接找出其规律,也很难直接分析 人jax来抓取

    解决:使用模拟浏览器运行方式来实现,这样就可以做到在浏览器中看到是什么样,抓取的源码就是什么样,也即可见即可爬,这样就不用管网页内部的javascript用了什么渲染页面,不管网页后台的Ajax接口到底有哪些参数。

    Python提供了许多模拟浏览器运行的库,如 Selenium、 Splash、 PyV8, Ghost等

    7.1 Selenium 的使用

    Selenium是一个 自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作, 同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬。

    基本使用

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.wait import WebDriverWait
    
    browser = webdriver.Chrome()
    try:
        browser.get('https://www.baidu.com')
        input = browser.find_element_by_id('kw')  # 找到输入框,根据id获取
        input.send_keys('Python')
        input.send_keys(Keys.ENTER)
        wait = WebDriverWait(browser,10)
        wait.until(EC.presence_of_element_located((By.ID,'content_left'))) # 里面要是一个元祖
        print(browser.current_url)
        print(browser.get_cookies())
        print(browser.page_source)
    except:
        print('Fail')
    finally:
        browser.close()
    

      

    3. 声明浏览器对象

    from selenium import webdriver
    
    browser = webdriver.Chrome() # google
    browser = webdriver.Firefox() # 火狐
    browser = webdriver.Edge()
    browser = webdriver.PhantomJS() # 无界面
    browser = webdriver.Safari() # mac自带浏览器
    

      

    4. 访问页面

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    browser.get('https://www.taobao.com')  # 通过get(url)
    print(browser.page_source)
    browser.close()
    

      

    5. 查找节点

    目的:找到输入框

    ·单个节点

    淘宝

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    browser.get('https://www.taobao.com')
    input_first = browser.find_element_by_id('q')  # id方式
    input_second = browser.find_element_by_css_selector('#q') # css方式
    input_third = browser.find_element_by_xpath('//*[@id="q"]') # xpath方式
    print(input_first,input_second,input_third)
    browser.close()
    

    Selenium还提供了通用方法 find_element(),它需要传入两个参数: 查找方式 By和值。 实 际上,它就是 find_element_by_id()这种方法的通用函数版本,比如 find_element_by_id(id)就等价 于 find_element(By.ID, id) 

    ·多个节点 

     find_element() 只能查找目标网页中的一个,要查找多个要用find_elements()

    from selenium import  webdriver
    
    browser = webdriver.Chrome()
    browser.get('https://www.taobao.com')
    lis = browser.find_elements_by_css_selector('.service-bd li')
    print(lis)
    browser.close()
    

      

    6. 节点交互

    http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement

    输入文字:send_keys()

    清空文字:clear()

    点击按钮:click()

    from selenium import webdriver
    import time
    
    browser = webdriver.Chrome()
    browser.get('https://www.taobao.com')
    input = browser.find_element_by_id('q')
    input.send_keys('iphone')
    time.sleep(1)
    input.clear()
    input.send_keys('ipad')
    button = browser.find_element_by_class_name('btn-search')
    button.click()
    

      淘宝要登陆,后面的看不到结果

    7. 动作链

    执行鼠标拖曳 、 键盘按键等动作

    from selenium import webdriver
    from selenium.webdriver import ActionChains
    
    browser = webdriver.Chrome()
    url = 'http://www.runoob.com/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()

    8 .执行javascript

    对于没有提供的操作,如下拉进度条,可以使用excute_script()方法实现。

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    browser.get('https://www.zhihu.com/explore')
    browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
    browser.execute_script('alert("To Bottom")') # 下拉到底部后,谈出alert提示框
    

      

    9. 获取节点信息

    page_source 属性可以获取网页的源代码,然后通过Beautiful soup pyquery进行信息提取

    也可以使用selenium自带的获取属性、文本等

    · 获取属性

    get_attribute()方法来获取节点的属性

    from selenium import webdriver
    from selenium.webdriver import ActionChains
    
    browser = webdriver.Chrome()
    url = 'https://www.zhihu.com/explore'
    browser.get(url)
    logo = browser.find_element_by_id('zh-top-link-logo')
    print(logo)
    print(logo.get_attribute('class'))
    

      

    · 获取文本值

    text()

    ·获取 id、位置、 标签名和大小

    id 属性可以获取节点 id, location 属性可以获取该节点在页面中的相对位置, tag_name 属性可以获取标签名称, 就是宽高

    10. 切换 Frame

    网页中有一种节点叫作 iframe,也就是子 Frame,相当于页面的子页面,它的结构和外 部网页的结构完全一致。 Selenium打开页面后,它默认是在父级 Frame里面操作,而此时如果页面中 还有子 Frame,它是不能获取到子 Frame里面的节点的 。 这时就需要使用 switch_to.frame()方法来切 换 Frame

    11. 延时等待

    get()方法会在网页框架加载结束后结束执行,此时如果获取page_source,可能并不是浏览器完全加载完全的页面,如果有页面有额外的Ajax请求,在网页源代码中并不一定能成功获取,所以需要延时等待一下。

    ·隐式等待

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    browser.implicitly_wait(10)  # 隐式等待,默认等待0秒,找不到继续等一会在找,容易受到页面加载时间的影响
    browser.get('https://www.zhihu.com/explore')
    input = browser.find_element_by_class_name('zu-top-add-question')
    print(input)
    

      

    ·显式等待

    指定一个最长等待时间

    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
    
    browser = webdriver.Chrome()
    browser.get('https://www.taobao.com/')
    wait = WebDriverWait(browser,10)
    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)
    

    12. 前进和后退 

    forword() 前进下一个页面,back() 返回前一个页面

    13. Cookies 

    get_cookies() 方法获取所有的 Cookie 

    add_cookies() 方法添加cookies

    delete_all_cookies()方法删除所有的 Cookies 

    14. 选项卡管理 

    window handles 属性获取 当前开启 的所有选项卡 ,返回的是选项卡的代号列表 

    switch_to_window()方法 即可,其中参数是选项卡的代号  

    15. 异常处理 

    7.2 Splash 的使用

    splash是一个javascript渲染服务,是一个带有http轻量级浏览器,同时对接了python中的Twisted和QT库,同样可以实现动态渲染页面的抓取

    安装splash 要先安装docker,参考:http://www.runoob.com/docker/macos-docker-install.html

     

    查看docker创建的容器,docker ps -l

    查看创建的容器的映射 docker port 1defb38b57be

    如果退出来了,执行docker run -p 8050:8050 scrapinghub/splash

    4 Splash Lua脚本 

    function main(splash, args)  #main 方法是固定的
      splash:go("http://www.baidu.com")
      splash:wait(0.5)
      local title = splash:evaljs("document.title")
      return { #返回可以是字符串形式也可以是字典形式
        title = title
      }
    end
    

      

    5 . Splash 对象属性 

    • args 

    • js_enabled 将其配置为 true或 false来控制是否执行 JavaScript 代码,默认为 true

    • resource timeout 设置加载的超时时间,单位是秒 

    • images_enabled 设置图片是否加载,默认情况下是加载的。 禁用该属性后,可以节省网络流量并提高 网页加载速度 

    • plugins_enabled 控制浏览器插件(如 Flash插件)是否开启 。 默认情况下,此属性是 false 

    • scroll_position 设置此属性,我们可以控制页面上下或左右滚动 

    6. Splash 对象 的方法

    •go() 用来请求某个链接,而且它可以模拟 GET 和 POST请求,同时支持传入请求头、表单等数据 

    • wait() 控制页面的等待时间 

    • jsfunc() 可以直接调用 JavaScript定义的方法,但是所调用的方法需要用双中括号包围,这相当于 实现了 JavaScript方法到 Lua脚本的转换 

     • evaljs() 

    可以执行 JavaScript代码并返回最后一条 JavaScript语句的返回结果

    • runjs() 

    可以执行 JavaScript代码,它与 evaljs()的功能类似,但是更偏向于执行某些动作或声明 某些方法 

     • autoload() 

    此方法可以设置每个页面访问时自动加载的对象 

    7. Splash API 调用

    • render.html 

    用于获取javascripte渲染页面 的HTML代码,接口地址就是splash的运行地址加此接口名称。

    import requests
    
    curl = 'http://localhost:8050/render.html?url=http://www.baidu.com&wait=5' # 等待5秒
    response = requests.get(curl)
    print(response.text)
    

     

    • render.png 

    此接口可以获取网页截图,其参数比 render.html多了几个,比如通过 width 和 height 来控制宽高, 它返回的是 PNG格式的图片二进制数据 

    import requests
    
    url =  'http://localhost:8050/render.png?url=http://www.jd.com&wait=5&width=1000&height=700'
    response = requests.get(url)
    with open('jd.png','wb') as f:
        f.write(response.content)
    

      

    • render.bar 

    此接口用于获取页面加载的 HAR数据 

    • render.json 

    此接口包含了前面接口的所有功能,返回结果是 JSON格式 

    • execute

    此接口才是最为强大的接口 。 前面说了很多 SplashLua脚本的操作,用此接口便可实现与 Lua脚本的对接 。

    前面的 render.html和 render.png等接口对于一般的 JavaScript渲染页面是足够了 ,但是如果要实现

    一些交互操作的话,它们还是无能为力,这里就需要使用 execute接口。

    import requests
    from urllib.parse import quote
    
    lua = '''
    function main(splash)  # 这里没有冒号
        return 'hello'
    end
    '''
    
    url = 'http://localhost:8050/execute?lua_source=' + quote(lua) # 对脚本进行URL转码
    response = requests.get(url)
    print(response.text)
    

      

    7.3 Splash 负载均衡配置 

    7.4 使用 Selenium 爬取淘宝商品 

    淘宝,它的整个页面数据确实也是通过 Ajax获取的,但是这些 Ajax接口 参数比较复杂,可能会包含加密密钥等, 所以如果想自己构造 Ajax 参数,还是比较困难的 。 对于这 种页面 , 最方便快捷 的抓取方法就是通过 Selenium 

    商品列表信息

    # 爬取淘宝页面商品信息,包括商品名称、商品价格、购买人数、店铺名称、店铺所在地
    from selenium import webdriver
    from selenium.common.exceptions import TimeoutException
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.wait import WebDriverWait
    from urllib.parse import quote
    from pyquery import PyQuery as pq
    import json
    
    browser = webdriver.Chrome()
    wait = WebDriverWait(browser,10)
    KEYWORD = 'iPad'
    
    def index_page(page):
        print('正在爬取第',page,'页')
        try:
            url = 'https://www.taobao.com/search?q=' + quote(KEYWORD)
            browser.get(url)
            if page > 1: # 如果大于1,就进行跳转操作,否则等待页面加载完成
                input = wait.until(
                    EC.presence_of_element_located((By.CSS_SELECTOR,'#mainsrp-pager div.from>input'))
                )
                submit = wait.until(
                    EC.element_to_be_clickable((By.CSS_SELECTOR,'#mainsrp-pager div.from>span.btn.J_Submit'))
                )
                input.clear()
                input.send_keys(page)
                submit.click()
            wait.until(
                EC.text_to_be_present_in_element((By.CSS_SELECTOR,'#mainsrp-pager li.item.active>span'),str(page))
            )
            wait.until(
                EC.presence_of_element_located((By.CSS_SELECTOR,'.m-itemlist .items .item'))
            )
            get_products()
        except TimeoutException:
            index_page(page)
    
    def get_products():
        taobao_data = []
        html = browser.page_source
        doc = pq(html)
        items = doc('#mainsrp-itemlist .items .item').items()
        for item in items:
            product = {
                'image': item.find('.pic .img').attr('data-src'),
                'price': item.find('.price').text(),
                'deal': item.find('.title').text(),
                'shop': item.find('.shop').text(),
                'location': item.find('.location').text()
            }
            print(product)
            taobao_data.append(product)
        with open('taobao.json','a',encoding='utf-8') as f:
            f.write(json.dumps(taobao_data, indent=2, ensure_ascii=False))
    
    
    
    # MONGO_URL = 'localhost'
    # MONGO_DB = 'taobao'
    # MONGO_COLLECTION = 'products'
    # client = pymongo.MongoClient(MONGO_URL)
    # db = client[MONGO_DB]
    #
    # def save_to_mongo(result):
    #     try:
    #         if db[MONGO_COLLECTION].insert(result):
    #             print('存储到MongoBD 成功')
    #     except Exception:
    #         print('存储到MongoBD 失败')
    
    MAX_PAGR = 100
    def main():
        for i in range(1,MAX_PAGR+1):
            index_page(i)
    
    main()
    

      测试代码跑起来了,不过提示页面找不到了,没爬取成功,后面在补充学习

     

    嗷嗷

  • 相关阅读:
    cpp 模版函数
    叉积
    利用scrollTop 制作图片无缝滚动
    事件绑定和时间取消
    闭包写法
    增加类,删除类,查找类
    获取元素到页面上的位置
    在IE8中如何通过javascripts改变<style />中的内容?
    有关app的一些小知识
    获取页面高宽知识
  • 原文地址:https://www.cnblogs.com/zheng1076/p/11136609.html
Copyright © 2011-2022 走看看