一、基本知识与数据抓取
1.1通用爬虫和聚焦爬虫
通用爬虫
通用网络爬虫 是 捜索引擎抓取系统(Baidu、Google、Yahoo等)的重要组成部分。主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份。
通用性搜索引擎存在的一定的局限性:
-
通用搜索引擎所返回的结果都是网页,而大多情况下,网页里90%的内容对用户来说都是无用的。
-
不同领域、不同背景的用户往往具有不同的检索目的和需求,搜索引擎无法提供针对具体某个用户的搜索结果。
-
万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不同数据大量出现,通用搜索引擎对这些文件无能为力,不能很好地发现和获取。
-
通用搜索引擎大多提供基于关键字的检索,难以支持根据语义信息提出的查询,无法准确理解用户的具体需求。
聚焦爬虫
聚焦爬虫,是"面向特定主题需求"的一种网络爬虫程序,它与通用搜索引擎爬虫的区别在于: 聚焦爬虫在实施网页抓取时会对内容进行处理筛选,尽量保证只抓取与需求相关的网页信息。
而我们今后要学习的网络爬虫,就是聚焦爬虫。
1.2HTTP和HTTPS
HTTP协议
(HyperText Transfer Protocol,超文本传输协议):是一种发布和接收 HTML页面的方法。
HTTPS
(Hypertext Transfer Protocol over Secure Socket Layer)简单讲是HTTP的安全版,在HTTP下加入SSL层。
SSL
(Secure Sockets Layer 安全套接层)主要用于Web的安全传输协议,在传输层对网络连接进行加密,保障在Internet上数据传输的安全。
HTTP
的端口号为80
,HTTPS
的端口号为443
1.3python中bytes和str两种类型的区别
bytes
bytes对象只负责以二进制字节序列的形式记录所需记录的对象,至于该对象到底表示什么(比如到底是什么字符)则由相应的编码格式解码所决定
Python2 中 >>> type(b'xxxxx') <type 'str'> >>> type('xxxxx') <type 'str'>
Python3 中 >>> type(b'xxxxx') <class 'bytes'> >>> type('xxxxx') <class 'str'>
总结:bytes是Python 3中特有的,Python 2 里不区分bytes和str。
1.4使用Requests抓取数据
虽然Python的标准库中 urllib 模块已经包含了平常我们使用的大多数功能,但是它的 API 使用起来让人感觉不太好,而 Requests 自称 "HTTP for Humans",说明使用更简洁方便。
Requests 继承了urllib的所有特性。Requests支持HTTP连接保持和连接池,支持使用cookie保持会话,支持文件上传,支持自动确定响应内容的编码,支持国际化的 URL 和 POST 数据自动编码。
注意:requests 的底层实现其实就是 urllib
基本使用
1. 最基本的GET请求可以直接用get方法 response = requests.get("http://www.baidu.com/") # 也可以这么写 # response = requests.request("get", "http://www.baidu.com/") 2. 添加 headers 和 查询参数 如果想添加 headers,可以传入headers参数来增加请求头中的headers信息。如果要将参数放在url中传递,可以利用 params 参数。 import requests kw = {'wd':'长城'} headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"} # params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode() response = requests.get("http://www.baidu.com/s?", params = kw, headers = headers) # 查看响应内容,response.text 返回的是Unicode格式的数据 print (response.text) # 查看响应内容,response.content返回的字节流数据 print (respones.content) # 查看完整url地址 print (response.url) # 查看响应头部字符编码 print (response.encoding) # 查看响应码 print (response.status_code)
注意:
-
使用response.text 时,Requests 会基于 HTTP 响应的文本编码自动解码响应内容,大多数 Unicode 字符集都能被无缝地解码。
-
使用response.content 时,返回的是服务器响应数据的原始二进制字节流,可以用来保存图片等二进制文件。
通过requests获取网络上图片的大小
from io import BytesIO,StringIO import requests from PIL import Image img_url = "http://imglf1.ph.126.net/pWRxzh6FRrG2qVL3JBvrDg==/6630172763234505196.png" response = requests.get(img_url) f = BytesIO(response.content) img = Image.open(f) print(img.size)
理解一下 BytesIO 和StringIO
很多时候,数据读写不一定是文件,也可以在内存中读写。
StringIO顾名思义就是在内存中读写str。
BytesIO 就是在内存中读写bytes类型的二进制数据
基本POST请求(data参数)
1. 最基本post方法 response = requests.post("http://www.baidu.com/", data = data)
代理(proxies参数)
如果需要使用代理,你可以通过为任意请求方法提供 proxies 参数来配置单个请求: import requests # 根据协议类型,选择不同的代理 proxies = { "http": "http://12.34.56.79:9527", "https": "http://12.34.56.79:9527", } response = requests.get("http://www.baidu.com", proxies = proxies) print response.text 也可以通过本地环境变量 HTTP_PROXY 和 HTTPS_PROXY 来配置代理: export HTTP_PROXY="http://12.34.56.79:9527" export HTTPS_PROXY="https://12.34.56.79:9527"
私密代理验证(特定格式) 和 Web客户端验证(auth 参数) 私密代理 import requests # 如果代理需要使用HTTP Basic Auth,可以使用下面这种格式: proxy = { "http": "mr_mao_hacker:sffqry9r@61.158.163.130:16816" } response = requests.get("http://www.baidu.com", proxies = proxy) print (response.text) web客户端验证 如果是Web客户端验证,需要添加 auth = (账户名, 密码) import requests auth=('test', '123456') response = requests.get('http://192.168.199.107', auth = auth) print (response.text)
Cookies 如果一个响应中包含了cookie,那么我们可以利用 cookies参数拿到: import requests response = requests.get("http://www.baidu.com/") # 7. 返回CookieJar对象: cookiejar = response.cookies # 8. 将CookieJar转为字典: cookiedict = requests.utils.dict_from_cookiejar(cookiejar)
session
在 requests 里,session对象是一个非常常用的对象,这个对象代表一次用户会话:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开。
会话能让我们在跨请求时候保持某些参数,比如在同一个 Session 实例发出的所有请求之间保持 cookie 。
处理HTTPS请求 SSL证书验证
Requests也可以为HTTPS请求验证SSL证书: 要想检查某个主机的SSL证书,你可以使用 verify 参数(也可以不写) import requests response = requests.get("https://www.baidu.com/", verify=True) #要跳过证书设置为False # 也可以省略不写 # response = requests.get("https://www.baidu.com/")
-
requests.utils.unquot(url)
-
requests.utils.quot(url)
-
requests.get(url,timeout=3)
retrying模块如何使用
爬虫中遇到js生成的数据,怎么办
-
定位js
-
search all file搜索关键字
-
event listener
-
-
分析js,添加断点的方式
寻找登录页面post的地址
-
form表单的action的url地址
-
input标签中用户名密码的name的值作为键,真正的用户名密码作为值的一个字典
-
-
network 中抓包,找到post数据
-
form data中
-
参数的来源
-
响应中(当前的响应或者是其他的url地址的响应)
-
js生成
-
-
-
json模块如何使用,在一个文档中连续写入多个json,能够整体的读出来么
-
字符串和python类型
-
json.loads(json_str)
-
json.dumps(python类型,ensure_ascii=False,indent=2)
-
-
类文件对象中的数据和python类型的转化
-
json.load(fp)[fp是类文件对象]
-
json.dump(obj,fp,ensure_ascii=False,indent=2)
二、解析数据
2.1使用正则表达式
Python 的 re 模块
在 Python 中,我们可以使用内置的 re 模块来使用正则表达式。
有一点需要特别注意的是,正则表达式使用 对特殊字符进行转义,所以如果我们要使用原始字符串,只需加一个 r 前缀,示例:
r'chuanzhiboke . python'
re 模块的一般使用步骤如下:
-
使用
compile()
函数将正则表达式的字符串形式编译为一个Pattern
对象 -
通过
Pattern
对象提供的一系列方法对文本进行匹配查找,获得匹配结果,一个 Match 对象。 -
最后使用
Match
对象提供的属性和方法获得信息,根据需要进行其他的操作
2.2 使用xpath
什么是XPath?
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。
XPath 开发工具
- 开源的XPath表达式编辑工具:XMLQuire(XML格式文件可用)
- Chrome插件 XPath Helper
- Firefox插件 XPath Checker
选取节点
XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
下面列出了最常用的路径表达式:
使用xpath helper或者是chrome中的copy xpath都是从element中提取的数据,但是爬虫获取的是url对应的响应,往往和elements不一样 获取文本 a/text() 获取a下的文本 a//text() 获取a下的所有标签的文本 //a[text()='下一页'] 选择文本为下一页三个字的a标签 @符号 a/@href //ul[@id="detail-list"] // 在xpath最前面表示从当前html中任意位置开始选择 li//a 表示的是li下任何一个标签
-
lxml能够修正HTML代码,但是可能会改错了
-
使用etree.tostring观察修改之后的html的样子,根据修改之后的html字符串写xpath
-
-
lxml 能够接受bytes和str的字符串
-
提取页面数据的思路
-
先分组,渠道一个包含分组标签的列表
-
遍历,取其中每一组进行数据的提取,不会造成数据的对应错乱
总结:
lxml模块如何使用 - from lxml import etree - element = etree.HMTL(str,bytes) - element.xpath("xpath") - etree.tostring(element) #把element转化为字符串 xpath有哪些常用方法 - // 从任意位置选择节点 - //a/text() a下的文本 - //a//text() a下所有的文本 - . 当前路径 - @符号 - a/@href - div[@class='a'] - text() - a[text()='下一页'] - .. 上一级 - //a[1] - //a[last()] - //a[postion()<4] - //a[1]|//a[5] - a[contains(text(),"下一页")] queue模块如何使用 - from queue import Queue - 实例化 - queue.put() #get计数减一 - queue.get() - queue.task_doen() #计数减一 - queue.join() #让主线程阻塞
三、爬虫套路
-
准备url
-
准备start_url
-
url地址规律不明显,总数不确定
-
通过代码提取下一页的url
-
xpath
-
寻找url地址,部分参数在当前的响应中(比如,当前页码数和总的页码数在当前的响应中)
-
-
-
准备url_list
-
页码总数明确
-
url地址规律明显
-
-
-
发送请求,获取响应
-
添加随机的User-Agent,反反爬虫
-
添加随机的代理ip,反反爬虫
-
在对方判断出我们是爬虫之后,应该添加更多的headers字段,包括cookie
-
cookie的处理可以使用session来解决
-
准备一堆能用的cookie,组成cookie池
-
如果不登录
-
准备刚开始能够成功请求对方网站的cookie,即接收对方网站设置在response的cookie
-
下一次请求的时候,使用之前的列表中的cookie来请求
-
-
如果登录
-
准备多个账号
-
使用程序获取每个账号的cookie
-
之后请求登录之后才能访问的网站随机的选择cookie
-
-
-
-
提取数据
-
确定数据的位置
-
如果数据在当前的url地址中
-
提取的是列表页的数据
-
直接请求列表页的url地址,不用进入详情页
-
-
提取的是详情页的数据
-
确定url
-
-
发送请求
-
-
提取数据
-
-
返回
-
-
-
如果数据不在当前的url地址中
-
在其他的响应中,寻找数据的位置
-
从network中从上往下找
-
-
使用chrome中的过滤条件,选择出了js,css,img之外的按钮
-
-
使用chrome的search all file,搜索数字和英文
-
-
-
-
数据的提取
-
xpath,从html中提取整块的数据,先分组,之后每一组再提取
-
re,提取max_time,price,html中的json字符串
-
json
-
-
-
保存
-
保存在本地,text,json,csv
-
保存在数据库
四、动态HTML处理
Selenium介绍
Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium 可以直接运行在浏览器上,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器)。
Selenium 可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。
Selenium 自己不带浏览器,不支持浏览器的功能,它需要与第三方浏览器结合在一起才能使用。但是我们有时候需要让它内嵌在代码中运行,所以我们可以用一个叫 PhantomJS 的工具代替真实的浏览器。
可以从 PyPI 网站下载 Selenium库https://pypi.python.org/simple/selenium ,也可以用 第三方管理器 pip用命令安装:sudo pip install selenium Selenium 官方参考文档:http://selenium-python.readthedocs.io/index.html
快速入门
Selenium 库里有个叫 WebDriver 的 API。WebDriver 有点儿像可以加载网站的浏览器,但是它也可以像 BeautifulSoup 或者其他 Selector 对象一样用来查找页面元素,与页面上的元素进行交互 (发送文本、点击等),以及执行其他动作来运行网络爬虫。

# IPython2 测试代码 # 导入 webdriver from selenium import webdriver # 调用键盘按键操作时需要引入的Keys包 from selenium.webdriver.common.keys import Keys # 调用环境变量指定的PhantomJS浏览器创建浏览器对象 driver = webdriver.PhantomJS() # 如果没有在环境变量指定PhantomJS位置 # driver = webdriver.PhantomJS(executable_path="./phantomjs")) # get方法会一直等到页面被完全加载,然后才会继续程序,通常测试会在这里选择 time.sleep(2) driver.get("http://www.baidu.com/") # 获取页面名为 wrapper的id标签的文本内容 data = driver.find_element_by_id("wrapper").text # 打印数据内容 print data # 打印页面标题 "百度一下,你就知道" print driver.title # 生成当前页面快照并保存 driver.save_screenshot("baidu.png") # id="kw"是百度搜索输入框,输入字符串"长城" driver.find_element_by_id("kw").send_keys(u"长城") # id="su"是百度搜索按钮,click() 是模拟点击 driver.find_element_by_id("su").click() # 获取新的页面快照 driver.save_screenshot("长城.png") # 打印网页渲染后的源代码 print driver.page_source # 获取当前页面Cookie print driver.get_cookies() # ctrl+a 全选输入框内容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a') # ctrl+x 剪切输入框内容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x') # 输入框重新输入内容 driver.find_element_by_id("kw").send_keys("itcast") # 模拟Enter回车键 driver.find_element_by_id("su").send_keys(Keys.RETURN) # 清除输入框内容 driver.find_element_by_id("kw").clear() # 生成新的页面快照 driver.save_screenshot("itcast.png") # 获取当前url print driver.current_url # 关闭当前页面,如果只有一个页面,会关闭浏览器 # driver.close() # 关闭浏览器 driver.quit()
页面操作
# 获取id标签值 element = driver.find_element_by_id("passwd-id") # 获取name标签值 element = driver.find_element_by_name("user-name") # 获取标签名值 element = driver.find_elements_by_tag_name("input") # 也可以通过XPath来匹配 element = driver.find_element_by_xpath("//input[@id='passwd-id']")
定位UI元素 (WebElements)
关于元素的选取,有如下的API 单个元素选取
find_element_by_id
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
-
获取文本和获取属性
-
先定位到元素,然后调用
.text
或者get_attribute
方法来去
-
-
selenium获取的页面数据是浏览器中elements的内容
-
find_element和find_elements的区别
-
find_element返回一个element,如果没有会报错
-
find_elements返回一个列表,没有就是空列表
-
在判断是否有下一页的时候,使用find_elements来根据结果的列表长度来判断
-
-
如果页面中含有iframe、frame,需要先调用driver.switch_to.frame的方法切换到frame中才能定位元素
-
selenium请求第一页的时候回等待页面加载完了之后在获取数据,但是在点击翻页之后,hi直接获取数据,此时可能会报错,因为数据还没有加载出来,需要time.sleep(3)
-
selenium中find_element_by_class_name智能接收一个class对应的一个值,不能传入多个
总结:
selenium 如何使用 - from selenium import webdriver - driver = webdriver.Chrome() - driver.get(url) - driver.quit() selenium如何定位,如何获取属性和文本 - driver.find_element 返回一个对象,没有会报错 - driver.find_elements 返回一个列表,空列表 - driver.find_element_by_id() - driver.find_element_by_class_name() - driver.find_element_by_xpath() - driver.find_element_by_link_text() - driver.find_element_by_id().text - driver.find_element_by_id().get_attribute() selenium如何切换iframe中 - driver.switch_to.frame(frame的id,name,driver.find_element_by_xpath("//a[1]"))
五、验证码识别
-
-
请求验证码的地址,获得相应,识别
-
-
url不变,验证码会变
-
思路:对方服务器返回验证码的时候,会和每个用户的信息和验证码进行一个对应,之后,在用户发送post请求的时候,会对比post请求中法的验证码和当前用户真正的存储在服务器端的验证码是否相同
-
1.实例化session
-
2.使用seesion请求登录页面,获取验证码的地址
-
3.使用session请求验证码,识别
-
4.使用session发送post请求’
-
-
使用selenium登录,遇到验证码
-
url不变,验证码不变,同上
-
url不变,验证码会变
-
1.selenium请求登录页面,同时拿到验证码的地址
-
2.获取登录页面中driver中的cookie,交给requests模块发送验证码的请求,识别
-
3.输入验证码,点击登录
-
-