Python数据网络采集5--处理Javascript和重定向

到目前为止,我们和网站服务器通信的唯一方式,就是发出HTTP请求获取页面。有些网页,我们不需要单独请求,就可以和网络服务器交互(收发信息),那么这个网页可能采用了Ajax技术来加载数据。使用以前的采集方法,可能只能采集到加载之前的数据,重要的数据就抓不到了。

和Ajax一样,动态HTML(DHTML)也是一系列用于解决网络问题的技术集合。DHTML用客户端语言,如JavaScript控制页面的HTML元素。经常,在我们采集网站时,从浏览器中看到的内容,和爬取到的内容就是不一样。或者网页用一个加载页把我们引到另外一个页面上,但是URL链接在这个过程中却一直没有变化。

这些都是因为网页中的JavaScript在作怪。浏览器可以正确执行JavaScript,但是我们在爬取过程中,也许直接就忽视了这些代码。所以导致浏览器中看到的和爬取到的内容不一样。

Ajax/DHTML技术给爬虫带来了困难,不过可以使用Selenium可以方便处理页面中的JavaScript代码。

来看个例子, 下面访问的网页使用了Ajax技术加载,大概2秒左右页面内容会改变(但是地址栏的URL链接没有改变)。

import requests
from bs4 import BeautifulSoup

url = 'http://pythonscraping.com/pages/javascript/ajaxDemo.html'

r = requests.get(url)
soup = BeautifulSoup(r.text, 'lxml')
content = soup.find('div', id='content')
print(content.string)
This is some content that will appear on the page while it's loading. You don't care about scraping this.

Selenium处理JavaScript

实际上,如果在浏览器打开这个页面,最后显示的内容不是这样的。刚开始是会显示这些内容,不过马上就被新的内容取代了。可以试试等待个几秒。上面的例子中使用requests访问,是立即返回了响应的,所以只能获取到加载前的内容。所以若是要等待,requests好像就没那么好使了。上Selenium吧!

import time
from selenium import webdriver

driver = webdriver.PhantomJS(executable_path=r'C:\Program Files (x86)\phantomjs\bin\phantomjs.exe')
driver.get('http://pythonscraping.com/pages/javascript/ajaxDemo.html')
# 等待加载完成
time.sleep(5)
content = driver.find_element_by_id('content').text
print(content)

driver.quit()
Here is some important text you want to retrieve!
A button to click!

上面使用到了PhantomJs,就是一个没有界面的浏览器,和Selenium结合使用十分方便。PhantomJs需要下载。

这里是下载地址

WebElement有个属性text可以获得标签内的文本。看上面的打印信息,确实新的内容加载出来了。在使用时,需要指定phantomjs所在的目录。而且,因为没有界面,在使用完后记得close或者quit。

上面的代码限制了5秒后查找元素,但是页面到底什么时候加载好是不能确定的。所以可以不断检测页面某个内容是否已经加载完毕。

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

driver = webdriver.PhantomJS(executable_path=r'C:\Program Files (x86)\phantomjs\bin\phantomjs.exe')
driver.get('http://pythonscraping.com/pages/javascript/ajaxDemo.html')
try:
    element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'loadedButton')))
    print(element)
finally:

    print(driver.find_element_by_id('content').text)
    driver.close()
<selenium.webdriver.remote.webelement.WebElement (session="a300db00-6afa-11e7-9f0f-2189a7b4630b", element=":wdc:1500301053057")>
Here is some important text you want to retrieve!
A button to click!

这里使用了WebDriverWaitexpected_conditions来构成隐式等待。隐式等待就是等DOM中某个状态发生后再继续运行代码,没有明确的等待时间,但是有最大等待时限(上例中时10s),而显式等待就是指定了等待时间的,如前面有个例子指定了sleep(5)。expected_conditions指定了期望的条件,上面的例子是一直等待,直到id为loadedButton的元素显示出来。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"

其实下面两句的意思是一样的:

driver.find_element(By.ID, 'loadedButton')
driver.find_element_by_id('loadedButton')

Xpath语法

还能使用Xpath的语法进行查找。下面列举一些常用的语法。

  • /div 选择根节点是div的元素
  • //a 选择文档中的所有a节点(包括非根节点)
  • //@href 选择带有href属性的所有节点
  • //a[@href='https://www.google.com'] 选择所有具有href为google网站的a标签
  • //a[3] 选择文档中第3个a标签
  • //table[last()] 选择文档中最后一个table
  • //a[position() < 3] 选择文档中的前三个a标签

处理重定向

重定向分为客户端重定向(Redirect)和服务端重定向(Dispatch),后者意思为派遣,就是常说的转发。转发只请求了一次,所以Python的requests能轻松处理,但是重定向的话,请求了两次,url一般会变化。这时候需要使用Selenium了。下面的例子可以监视链接是否已经重定向了,使用的方法是,首先从页面开始加载的时候就监视DOM中的某一个元素,然后重复查找这个元素,检查它和原来的元素是不是同一个,直到抛出StaleElementReferenceException,就是说元素已经不在页面的DOM里了,此时已经跳转。

import time
# Stale means the element no longer appears on the DOM of the page
from selenium.common.exceptions import StaleElementReferenceException
from selenium import webdriver

def wait_for_load(a_driver):
    element = a_driver.find_element_by_tag_name('html')
    print('content', element)
    count = 0
    while True:
        count += 1
        # 超过10s,直接返回
        if count > 20:
            print('Timing out after 10s and returning')
            return

        time.sleep(0.5)  # 检查还是不是同一个element,如果不是,说明这个html标签已经不再DOM中了。如果不是抛出异常
        new = a_driver.find_element_by_tag_name('html')
        print('new', new)
        if element != new:
            raise StaleElementReferenceException('刚才重定向了!')

driver = webdriver.PhantomJS(r'C:\Program Files (x86)\phantomjs\bin\phantomjs.exe')
driver.get('https://pythonscraping.com/pages/javascript/redirectDemo1.html')
try:
    wait_for_load(driver)
except StaleElementReferenceException as e:
    print(e.msg)
finally:
    print(driver.page_source)
content <selenium.webdriver.remote.webelement.WebElement (session="e9c5e030-6b04-11e7-9cea-c913b202710e", element=":wdc:1500305464563")>
new <selenium.webdriver.remote.webelement.WebElement (session="e9c5e030-6b04-11e7-9cea-c913b202710e", element=":wdc:1500305464563")>
new <selenium.webdriver.remote.webelement.WebElement (session="e9c5e030-6b04-11e7-9cea-c913b202710e", element=":wdc:1500305464563")>
new <selenium.webdriver.remote.webelement.WebElement (session="e9c5e030-6b04-11e7-9cea-c913b202710e", element=":wdc:1500305464563")>
new <selenium.webdriver.remote.webelement.WebElement (session="e9c5e030-6b04-11e7-9cea-c913b202710e", element=":wdc:1500305464563")>
new <selenium.webdriver.remote.webelement.WebElement (session="e9c5e030-6b04-11e7-9cea-c913b202710e", element=":wdc:1500305464563")>
new <selenium.webdriver.remote.webelement.WebElement (session="e9c5e030-6b04-11e7-9cea-c913b202710e", element=":wdc:1500305464563")>
new <selenium.webdriver.remote.webelement.WebElement (session="e9c5e030-6b04-11e7-9cea-c913b202710e", element=":wdc:1500305468142")>
刚才重定向了!
<html><head>
<title>The Destination Page!</title>

</head>
<body>
This is the page you are looking for!

</body></html>

我们打印了刚刚进入网页时候的html元素对应的WebElement的wdc。wdc:1500305464563相当于是个id。

在循环中不断检测是否和原来的WebElement是否为同一个,如果不是,那么已经发生了重定向。发生重定向前,wdc已经改变为wdc:1500305468142。之后页面跳转到了redirectDemo2.html


by @sunhaiyu

2017.7.17

Python数据网络采集5--处理Javascript和重定向的更多相关文章

  1. API例子:用Python驱动Firefox采集网页数据

    1,引言 本文讲解怎样用Python驱动Firefox浏览器写一个简易的网页数据采集器.开源Python即时网络爬虫项目将与Scrapy(基于twisted的异步网络框架)集成,所以本例将使用Scra ...

  2. (数据科学学习手札50)基于Python的网络数据采集-selenium篇(上)

    一.简介 接着几个月之前的(数据科学学习手札31)基于Python的网络数据采集(初级篇),在那篇文章中,我们介绍了关于网络爬虫的基础知识(基本的请求库,基本的解析库,CSS,正则表达式等),在那篇文 ...

  3. (数据科学学习手札47)基于Python的网络数据采集实战(2)

    一.简介 马上大四了,最近在暑期实习,在数据挖掘的主业之外,也帮助同事做了很多网络数据采集的内容,接下来的数篇文章就将一一罗列出来,来续写几个月前开的这个网络数据采集实战的坑. 二.马蜂窝评论数据采集 ...

  4. (数据科学学习手札33)基于Python的网络数据采集实战(1)

    一.简介 前面两篇文章我们围绕利用Python进行网络数据采集铺垫了很多内容,但光说不练是不行的,于是乎,本篇就将基于笔者最近的一项数据需求进行一次网络数据采集的实战: 二.网易财经股票数据爬虫实战 ...

  5. (数据科学学习手札31)基于Python的网络数据采集(初级篇)

    一.简介 在实际的业务中,我们手头的数据往往难以满足需求,这时我们就需要利用互联网上的资源来获取更多的补充数据,但是很多情况下,有价值的数据往往是没有提供源文件的直接下载渠道的(即所谓的API),这时 ...

  6. 使用python访问网络上的数据

    这两天看完了Course上面的: 使用 Python 访问网络数据 https://www.coursera.org/learn/python-network-data/ 写了一些作业,完成了一些作业 ...

  7. python数据可视化-matplotlib入门(7)-从网络加载数据及数据可视化的小总结

    除了从文件加载数据,另一个数据源是互联网,互联网每天产生各种不同的数据,可以用各种各样的方式从互联网加载数据. 一.了解 Web API Web 应用编程接口(API)自动请求网站的特定信息,再对这些 ...

  8. Python即时网络爬虫项目启动说明

    作为酷爱编程的老程序员,实在按耐不下这个冲动,Python真的是太火了,不断撩拨我的心. 我是对Python存有戒备之心的,想当年我基于Drupal做的系统,使用php语言,当语言升级了,推翻了老版本 ...

  9. 【Github搬砖】Python入门网络爬虫之精华版

    Python学习网络爬虫主要分3个大的版块:抓取,分析,存储 另外,比较常用的爬虫框架Scrapy,这里最后也详细介绍一下. 首先列举一下本人总结的相关文章,这些覆盖了入门网络爬虫需要的基本概念和技巧 ...

随机推荐

  1. Easyui设置动态表格,动态导出数据实例,附Dome

    最近碰到一个需求,需要提供一个弹出页面选择列表页面需要显示的列,页面确认之后需要修改列表页面显示的表格,导出的数据也需要同步变化. 下面直接上代码 1.设置需要显示的列columus为全局对象,用于子 ...

  2. 编译安装nginx却requires the PCRE library

    编译安装nginx需要pcre包,未安装会有如下提示: ./configure: error: the HTTP rewrite module requires the PCRE library. Y ...

  3. v9更换域名

    网站在发展的过程中,很可能多次的修改域名.那么在PHPCMS V9中我们要怎么进行设置呢? 请进行以下步骤的修改: 修改/caches/configs/system.php里面所有和域名有关的,把以前 ...

  4. 在C#中初遇Socket - 1

    后期项目实战:多人在线聊天室 源码位置:https://git.oschina.net/z13qu/BlogProjects 课前须知 这里不讲解Socket和TCP/IP的底层知识 对于初学者来说底 ...

  5. 1.Google Chrome浏览器 控制台全解析

    Google Chrome浏览器 控制台全解析 在Google Chrome浏览器出来之前,我一直使用FireFox,因为FireFox的插件非常丰富,更因为FireFox有强大的Firebug,对于 ...

  6. 使用Gulp实现前端构建自动化

    使用Gulp实现前端构建自动化 安装 一.安装NodeJs Gulp的安装依赖于NodeJs的npm安装管理器 安装包下载地址 关于npm命令: 1. npm install <name> ...

  7. Chrome控制台使用详解

    Chrome的开发者工具已经强大到没朋友的地步了,特别是其功能丰富界面友好的console,使用得当可以有如下功效: 更高「逼格」更快「开发调试」更强「进阶级的Frontender」 Bug无处遁形「 ...

  8. 没有在xml中引入 相关的配置文件

    错误信息如下   严重: Servlet.service() for servlet AutoReplyServlet threw exception org.apache.ibatis.except ...

  9. jquery 变量和原生js变量的关系

    其实js 变量和 jquery没什么不一样, 也可以直接 var  hhhh=$("header"); 但是当用到用到hhh时依然要用jquery 的方式,而不能js原生的方式.

  10. (转载)Java内部类的使用小结

    原文摘自:http://android.blog.51cto.com/268543/384844/   内部类是指在一个外部类的内部再定义一个类.类名不需要和文件夹相同. *内部类可以是静态stati ...