使用selenium爬取百度文库文章(动态)
运行平台: Windows
Python版本: Python3.x
IDE: Sublime text3
- 前言
- 问题分析
- 预备知识
- 1 Selenium
- 11 简介
- 12 安装
- 13 基础知识
- 131 小试牛刀
- 132 模拟提交
- 133 元素选取
- 134 界面交互
- 135 添加User-Agent
- 2 Xpath
- 1 Selenium
- 动手实战
- 1 页面切换
- 2 内容爬取
- 3 整体代码
- 总结
1 前言
大家都应该有过从百度文库下载东西的经历,对于下载需要下载券的文章,我们可以办理文库VIP(土豪的选择):
有的人也会在某宝购买一定的下载券,然后进行下载。而另一些勤勤恳恳的人,则会选择上传文章,慢慢攒下载券。任劳任怨的人,则会自己一点一点的复制粘贴,复制到word里文字太大,那就复制到txt文件里。而既不想花钱又不想攒下载券,也不想一点一点复制粘贴的人,会选择“冰点文库”这样的下载软件,不过貌似现在“冰点文库”已经不能使用了。当然,还有一些其他破解方法,比如放到手机的百度文库APP里,另存为文章,不需要下载券就可以下载文章。诸如此类的方法,可谓五花八门。而对于学习爬虫的人来说,面对怎样免费下载一个付费的word文章的问题,第一个想到的应该就是:自己写个程序搞下来。
2 问题分析
我们以如何下载下面这篇文章为例,分析问题:
URL : https://wenku.baidu.com/view/aa31a84bcf84b9d528ea7a2c.html
我想,看到这样的一个文章,如果爬取当前页面的内容还是很好爬的吧。感觉so easy!至少我当时是这么想的,但是当把文章翻到最下方的时候,我看到了如下内容:
呃….需要点击“继续阅读”才能显示后续的内容,我单爬这一页内容,是爬不到后续的内容的。第一个想到的方法是,抓包分析下,然后我又一次蒙逼了:
Request URL这么长!!最后的expire时间信息好解决,其他的信息呢?不想做无谓的挣扎,因此,我果断地放弃这个方法。
问题:获取当前页的内容好办,怎么获取接下来页面的内容?
带着这个思考,Selenium神器走入了我的视线。
3 预备知识
3.1 Selenium
3.1.1 简介
Selenium 是什么?一句话,自动化测试工具。它支持各种浏览器,包括 Chrome,Safari,Firefox 等主流界面式浏览器,如果你在这些浏览器里面安装一个 Selenium 的插件,那么便可以方便地实现Web界面的测试。换句话说叫 Selenium 支持这些浏览器驱动。Selenium支持多种语言开发,比如 Java,C,Ruby等等,而对于Python,当然也是支持的!
3.1.2 安装
pip3 install selenium
- 1
3.1.3 基础知识
详细内容可查看官网文档:http://selenium-python.readthedocs.io/index.html
3.1.3.1 小试牛刀
我们先来一个小例子感受一下 Selenium,这里我们用 Chrome 浏览器来测试。
from selenium import webdriverbrowser = webdriver.Chrome()
browser.get('http://www.baidu.com/')
- 1
- 2
- 3
- 4
运行这段代码,会自动打开浏览器,然后访问百度。
如果程序执行错误,浏览器没有打开,那么应该是没有装 Chrome 浏览器或者 Chrome 驱动没有配置在环境变量里。下载驱动,然后将驱动文件路径配置在环境变量即可。
驱动下载地址:https://sites.google.com/a/chromium.org/chromedriver/downloads
windows下设置环境变量的方法:
win+r
,输入sysdm.cpl
,点击确定,出现如下对话框:
选择高级->环境变量。在系统变量的Path变量中,添加驱动文件路径即可(注意:分号)。
Linux的环境变量也好设置,在~/.bashrc
文件中export即可,记得source ~/.bashrc
。
当然,你不设置环境变量也是可以的,程序可以这样写:
from selenium import webdriverbrowser = webdriver.Chrome('path\to\your\chromedriver.exe')
browser.get('http://www.baidu.com/')
- 1
- 2
- 3
- 4
上面的path\to\your\chromedriver.exe
是你的chrome驱动文件位置,可以使用绝对路径。我们通过驱动的位置传递参数,也可以调用驱动,结果如下图所示:
3.1.3.2 模拟提交
下面的代码实现了模拟提交提交搜索的功能,首先等页面加载完成,然后输入到搜索框文本,点击提交,然后使用page_source打印提交后的页面的信息。
from selenium import webdriver
from selenium.webdriver.common.keys import Keysdriver = webdriver.Chrome()
driver.get("http://www.python.org")
assert "Python" in driver.title
elem = driver.find_element_by_name("q")
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
print(driver.page_source)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
全自动的哦,程序操控!是不是很酷炫?
其中 driver.get 方法会打开请求的URL,WebDriver 会等待页面完全加载完成之后才会返回,即程序会等待页面的所有内容加载完成,JS渲染完毕之后才继续往下执行。注意:如果这里用到了特别多的 Ajax 的话,程序可能不知道是否已经完全加载完毕。
WebDriver 提供了许多寻找网页元素的方法,譬如 find_element_by_*
的方法。例如一个输入框可以通过 find_element_by_name
方法寻找 name 属性来确定。
然后我们输入来文本然后模拟点击了回车,就像我们敲击键盘一样。我们可以利用 Keys 这个类来模拟键盘输入。
最后最重要的一点是可以获取网页渲染后的源代码。通过,输出 page_source
属性即可。这样,我们就可以做到网页的动态爬取了。
3.1.3.3 元素选取
关于元素的选取,有如下API:
单个元素选取:
find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
多个元素选取:
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
- 1
- 2
- 3
- 4
- 5
- 6
- 7
另外还可以利用 By 类来确定哪种选择方式:
from selenium.webdriver.common.by import Bydriver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_elements(By.XPATH, '//button')
- 1
- 2
- 3
- 4
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"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这些方法跟JavaScript的一些方法有相似之处,find_element_by_id
,就是根据标签的id属性查找元素,find_element_by_name
,就是根据标签的name属性查找元素。举个简单的例子,比如我想找到下面这个元素:
<input type="text" name="passwd" id="passwd-id" />
- 1
我们可以这样获取它:
element = driver.find_element_by_id("passwd-id")
element = driver.find_element_by_name("passwd")
element = driver.find_elements_by_tag_name("input")
element = driver.find_element_by_xpath("//input[@id='passwd-id']")
- 1
- 2
- 3
- 4
前三个都很好理解,最后一个xpath什么意思?这个无需着急,xpath是非常强大的元素查找方式,使用这种方法几乎可以定位到页面上的任意元素,在后面我会进行单独讲解。
3.1.3.4 界面交互
通过元素选取,我们能够找到元素的位置,我们可以根据这个元素的位置进行相应的事件操作,例如输入文本框内容、鼠标单击、填充表单、元素拖拽等等。由于篇幅原因,我就不一一讲解了,主要讲解本次实战用到的鼠标单击,更详细的内容,可以查看官方文档。
elem = driver.find_element_by_xpath("//a[@data-fun='next']")
elem.click()
- 1
- 2
比如上面这句话,我使用find_element_by_xpath()
找到元素位置,暂且不用理会这句话什么意思,暂且理解为找到了一个按键的位置。然后我们使用click()方法,就可以触发鼠标左键单击事件。是不是很简单?但是有一点需要注意,就是在点击的时候,元素不能有遮挡。什么意思?就是说我在点击这个按键之前,窗口最好移动到那里,因为如果这个按键被其他元素遮挡,click()就触发异常。因此稳妥起见,在触发鼠标左键单击事件之前,滑动窗口,移动到按键上方的一个元素位置:
page = driver.find_elements_by_xpath("//div[@class='page']")
driver.execute_script('arguments[0].scrollIntoView();', page[-1]) #拖动到可见的元素去
- 1
- 2
上面的代码,就是将窗口滑动到page这个位置,在这个位置,我们能够看到我们需要点击的按键。
3.1.3.5 添加User-Agent
使用webdriver,是可以更改User-Agent的,代码如下:
from selenium import webdriveroptions = webdriver.ChromeOptions()
options.add_argument('user-agent="Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"')
driver = webdriver.Chrome(chrome_options=options)
driver.get('https://www.baidu.com/')
- 1
- 2
- 3
- 4
- 5
- 6
使用Android的User-Agent打开浏览器,画风是这样的(第二条新闻的图片略劲爆):
Selenium就先介绍这么多,对于本次实战内容,已经足够。那么接下来,让我们聊聊xpath。
3.2 Xpath
这个方法是非常强大的元素查找方式,使用这种方法几乎可以定位到页面上的任意元素。在正式开始使用XPath进行定位前,我们先了解下什么是XPath。XPath是XML Path的简称,由于HTML文档本身就是一个标准的XML页面,所以我们可以使用XPath的语法来定位页面元素。
假设我们现在以图所示HTML代码为例,要引用对应的对象,XPath语法如下:
绝对路径写法(只有一种),写法如下:
引用页面上的form元素(即源码中的第3行):
/html/body/form[1]
- 1
注意:
元素的xpath绝对路径可通过firebug直接查询。
一般不推荐使用绝对路径的写法,因为一旦页面结构发生变化,该路径也随之失效,必须重新写。
绝对路径以单/号表示,而下面要讲的相对路径则以
//
表示,这个区别非常重要。另外需要多说一句的是,当xpath的路径以/
开头时,表示让Xpath解析引擎从文档的根节点开始解析。当xpath路径以//
开头时,则表示让xpath引擎从文档的任意符合的元素节点开始进行解析。而当/
出现在xpath路径中时,则表示寻找父节点的直接子节点,当//
出现在xpath路径中时,表示寻找父节点下任意符合条件的子节点,不管嵌套了多少层级(这些下面都有例子,大家可以参照来试验)。弄清这个原则,就可以理解其实xpath的路径可以绝对路径和相对路径混合在一起来进行表示,想怎么玩就怎么玩。
下面是相对路径的引用写法:
查找页面根元素:
//
查找页面上所有的input元素:
//input
查找页面上第一个form元素内的直接子input元素(即只包括form元素的下一级input元素,使用绝对路径表示,单/号):
//form[1]/input
查找页面上第一个form元素内的所有子input元素(只要在form元素内的input都算,不管还嵌套了多少个其他标签,使用相对路径表示,双//号):
//form[1]//input
查找页面上第一个form元素:
//form[1]
查找页面上id为loginForm的form元素:
//form[@id='loginForm']
查找页面上具有name属性为username的input元素:
//input[@name='username']
查找页面上id为loginForm的form元素下的第一个input元素:
//form[@id='loginForm']/input[1]
查找页面具有name属性为contiune并且type属性为button的input元素:
//input[@name='continue'][@type='button']
查找页面上id为loginForm的form元素下第4个input元素:
//form[@id='loginForm']/input[4]
Xpath功能很强大,所以也可以写得更加复杂一些,如下面图所示的HTML源码。
//*[@id='J_login_form']/dl/dt/input[@id='J_password']
- 1
也可以写成:
//*[@id='J_login_form']/*/*/input[@id='J_password']
- 1
这里解释一下,其中//*[@id=’ J_login_form’]
这一段是指在根元素下查找任意id为J_login_form
的元素,此时相当于引用到了form元素。后面的路径必须按照源码的层级依次往下写。按照图(3)所示代码中,我们要找的input元素包含在一个dt标签内,而dt又包含在dl标签内,所以中间必须写上dl和dt两层,才到input这层。当然我们也可以用*号省略具体的标签名称,但元素的层级关系必须体现出来,比如我们不能写成//*[@id='J_login_form']/input[@id='J_password']
,这样肯定会报错的。
前面讲的都是xpath中基于准确元素属性的定位,其实xpath作为定位神器也可以用于模糊匹配。本次实战,可以进行准确元素定位,因此就不讲模糊匹配了。如果有兴趣,可以自行了解。
4 动手实战
以上面提到的文章为例,进行爬取讲解。URL : https://wenku.baidu.com/view/aa31a84bcf84b9d528ea7a2c.html
4.1 页面切换
由于网页的百度文库页面复杂,可能抓取内容不全,因此使用User-Agent,模拟手机登录,然后打印文章标题,文章页数,并进行翻页。先看下这个网站。
我们需要找到两个元素的位置,一个是页码元素的位置,我们根据这个元素的位置,将浏览器的滑动窗口移动到这个位置,这样就可以避免click()下一页元素的时候,有元素遮挡。然后找到下一页元素的位置,然后根据下一页元素的位置,触发鼠标左键单击事件。
我们审查元素看一下,这两个元素:
我们根据这两个元素,就可以通过xpath查找元素位置,代码分别如下:
page = driver.find_elements_by_xpath("//div[@class='page']")
nextpage = driver.find_element_by_xpath("//a[@data-fun='next']")
- 1
- 2
由于page元素有很多,所以我们使用find_elements_by_xpath()方法查找,然后使用page[-1],也就是链表中的最后一个元素的信息进行浏览器窗口滑动,代码如下:
from selenium import webdriveroptions = webdriver.ChromeOptions()
options.add_argument('user-agent="Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"')
driver = webdriver.Chrome(chrome_options = options)
driver.get('https://wenku.baidu.com/view/aa31a84bcf84b9d528ea7a2c.html')
page = driver.find_elements_by_xpath("//div[@class='page']")
driver.execute_script('arguments[0].scrollIntoView();', page[-1]) #拖动到可见的元素去
nextpage = driver.find_element_by_xpath("//a[@data-fun='next']")
nextpage.click()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
运行效果,自动翻页了有木有!
4.2 内容爬取
爬取内容这里,使用之前重点讲过的BeautifulSoup就可以。这里不再细奖,审查元素,自己分析下就有了。代码如下:
from selenium import webdriver
from bs4 import BeautifulSoupoptions = webdriver.ChromeOptions()
options.add_argument('user-agent="Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"')
driver = webdriver.Chrome(chrome_options=options)
driver.get('https://wenku.baidu.com/view/aa31a84bcf84b9d528ea7a2c.html')html = driver.page_source
bf1 = BeautifulSoup(html, 'lxml')
result = bf1.find_all(class_='rtcspage')
for each_result in result:bf2 = BeautifulSoup(str(each_result), 'lxml')texts = bf2.find_all('p')for each_text in texts:main_body = BeautifulSoup(str(each_text), 'lxml')for each in main_body.find_all(True):if each.name == 'span':print(each.string.replace('\xa0',''),end='')elif each.name == 'br':print('')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
爬取结果如下:
爬取的内容还是蛮规整的,对吧?
4.3 整体代码
我们能够翻页,也能够爬取当前页面内容,代码稍作整合,就可以爬取所有页面的内容了!找下网页的规律就会发现,5页文章放在一个网页里。思路:爬取正文内容,再根据爬取到的文章页数,计算页数/5.0,得到一个分数,如果这个分数大于1,则翻页继续爬,如果小于或等于1,代表到最后一页了。停止翻页。有一点注意一下,翻页之后,等待延时一下,等待页面加载之后在爬取内容,这里,我们使用最简单的办法,用sleep()进行延时。因此总体代码如下:
# -*- coding:UTF-8 -*-
from selenium import webdriver
from bs4 import BeautifulSoup
import reif __name__ == '__main__':options = webdriver.ChromeOptions()options.add_argument('user-agent="Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"')driver = webdriver.Chrome(chrome_options=options)driver.get('https://wenku.baidu.com/view/aa31a84bcf84b9d528ea7a2c.html')html = driver.page_sourcebf1 = BeautifulSoup(html, 'lxml')result = bf1.find_all(class_='rtcspage')bf2 = BeautifulSoup(str(result[0]), 'lxml')title = bf2.div.div.h1.stringpagenum = bf2.find_all(class_='size')pagenum = BeautifulSoup(str(pagenum), 'lxml').span.stringpagepattern = re.compile('页数:(\d+)页')num = int(pagepattern.findall(pagenum)[0])print('文章标题:%s' % title)print('文章页数:%d' % num)while True:num = num / 5.0html = driver.page_sourcebf1 = BeautifulSoup(html, 'lxml')result = bf1.find_all(class_='rtcspage')for each_result in result:bf2 = BeautifulSoup(str(each_result), 'lxml')texts = bf2.find_all('p')for each_text in texts:main_body = BeautifulSoup(str(each_text), 'lxml')for each in main_body.find_all(True):if each.name == 'span':print(each.string.replace('\xa0',''),end='')elif each.name == 'br':print('')print('\n')if num > 1:page = driver.find_elements_by_xpath("//div[@class='page']")driver.execute_script('arguments[0].scrollIntoView();', page[-1]) #拖动到可见的元素去nextpage = driver.find_element_by_xpath("//a[@data-fun='next']")nextpage.click()time.sleep(3)else:break
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
运行结果:
瞧,最后一页的内容也爬取下来了,接下来的工作就简单了,把这个结果写到txt文件中,我这里就不再进行讲解了。
至此,整篇的内容,我们都爬取下来了。是不是很酷?那就开始动手实践吧!
5 总结
这样爬取是可以爬取到内容,但是缺点也很明显:
没有处理图片内容,可以后续完善;
代码通用性不强,有的文章结构不是这样,需要对代码进行略微修改,才能爬取到内容;
对于上百页的内容爬取有些问题,翻页方式变了,需要换种方法处理,有兴趣的可以自己看下;
等待页面切换方法太out,可以使用显示等待的方式,等待页面加载;
selenium虽好,但是有些耗时,可以使用PhantomJS对这部分代码进行替换;
最后,我感觉我的方法可能有些low,如果有更好的方法,欢迎交流。
PS:如果觉得本篇本章对您有所帮助,欢迎关注、评论、顶!
原作链接:http://blog.csdn.net/c406495762/article/details/72331737
使用selenium爬取百度文库文章(动态)相关推荐
- 使用python中的Selenium爬取百度文库word文章
参考文章:Python3网络爬虫(九):使用Selenium爬取百度文库word文章,链接为: https://blog.csdn.net/c406495762/article/details/723 ...
- 爬取百度学术文章及文本挖掘分析
学了一段时间的爬虫,给自己找一个小项目来练练手,爬取百度百科文章,之后结合自然语言处理分析文本之间的相似度和提取所有文章的重要信息. 目标总览 1. 爬取数据(selenium + Beautif ...
- 写一个爬虫,可以爬取百度文库内容
爬取百度文库内容需要使用爬虫技术.以下是一个简单的 Python 爬虫示例: import requestsurl ="https://wenku.baidu.com/view/your_d ...
- python爬取qq好友_Python3实现QQ机器人自动爬取百度文库的搜索结果并发送给好友(主要是爬虫)...
一.效果如下: 二.运行环境: win10系统:python3:PyCharm 三.QQ机器人用的是qqbot模块 用pip安装命令是: pip install qqbot (前提需要有request ...
- python 爬虫——爬取百度文库VIP内容
转载自:爬取百度文库 代码实现 import requests import re import json import ossession = requests.session()def fetch ...
- python生成QQ机器人爬取百度文库链接推送好友并生成词云
QQ机器人爬取百度文库链接推送好友并生成词云 一.环境准备 二.实现QQ机器人 1.QQ机器人介绍 2.安装方法 3.实现自己的QQ机器人 三.百度文库内容链接爬取推送好友 代码实现: 思路分析 1. ...
- python爬取加密qq空间_使用python+selenium爬取qq空间好友动态
使用python+selenium爬取qq空间好友动态 分析过程如下: 要想用selenium登陆qq空间,必须点击账号密码登陆按钮然后再填写账号密码登陆. 1.PNG 点击账号密码按钮后跳转到如下页 ...
- go爬取json_Python爬取百度文库学习
首先先分享下github这篇文章吧,参考了部分代码,但我想做一个很详细的解读. 新版百度文库爬虫 · Jack-Cherish/python-spider@3c5480dgithub.com 首先, ...
- selenium爬取百度地图两点时间
老师提出了一个tsp问题的实战项目,让我们自己爬南京40多家景点间的距离去做.为此我自学了selenium库,花了一天时间编出了一段简单的爬取百度地图页面的代码. selenium的安装和驱动安装十分 ...
- Python爬虫之爬取某文库文章
文章目录 说在前面的话: 一.源码展示: 二.完整代码展示及运行效果图 **1.完整代码:** **2.效果图:** 三.解析源码: **1. 问题解决:** **2.自动登录:** **3.问题解决 ...
最新文章
- 人生第一个快速幂的题(HDU - 1097--A hard puzzle )
- ASP.NET中 Repeater嵌套
- 【算法】anchor free 和 anchor based 目标检测模型
- 记一次Mysql误删记录的挽救
- .NET Core ORM 类库Petapoco中对分页Page添加Order By对查询的影响
- git多人合作时,恢复误删文件
- orc parquet区别 spark_HIVE存储格式ORC、PARQUET对比
- 亲密关系沟通-【表达情绪】如何说出感受却不伤人
- html制作翻牌游戏,基于javascript实现句子翻牌网页版小游戏
- Sybase数据库知识总结
- 复制虚拟机出现”适配器 的mac地址在保留地址范围内‘’
- 受力分析软件_基于非线性分析的建筑结构设计与优化
- asp.net使用MailMessage发送邮件的方法
- Android集成FBReader(精简版)指南
- i7 8700k 安装linux,Intel i7 8700K核显UHD620安装黑苹果仿冒HD620方法
- C#删除word页眉页脚和最后一页
- ear的英语怎么念_不一样的高阶美式英语视频字母表 - A到I
- antlr 安装使用
- python3入门笔记
- 程序员压力大?常灸这七大穴位减轻你的压力!