Py007-01-12selenium使用

selenium使用

  • 问题:处理页面动态加载数据的爬取
  1. selenium
  2. phantomJs

selenium安装

三方库,可以实现让浏览器完成自动化操作

  • 环境搭建
1
2
3
4
5
6
7
# 1 安装依赖
pip install selenium
# 2 获取某一款浏览器的驱动程序(以谷歌浏览器为例)
# 谷歌浏览器驱动下载地址:http://chromedriver.storage.googleapis.com/index.html
#
下载的驱动程序必须和浏览器的版本统一,大家可以根据http://blog.csdn.net/huilan_same/article/details/51896672
中提供的版本映射表进行对应

代码介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
#导包
from selenium import webdriver
#创建浏览器对象,通过该对象可以操作浏览器
browser = webdriver.Chrome('驱动路径')
#使用浏览器发起指定请求
browser.get(url)

#使用下面的方法,查找指定的元素进行操作即可
find_element_by_id 根据id找节点
find_elements_by_name 根据name找
find_elements_by_xpath 根据xpath查找
find_elements_by_tag_name 根据标签名找
find_elements_by_class_name 根据class名字查找

自动化打开百度搜索关键字后页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 编码流程
from selenium import webdriver
from time import sleep
# 创建一个浏览器对象 executable_path为驱动的路径
bro = webdriver.Chrome(executable_path='./chromedriver')
# get 方法可以指定一个url,让浏览器进行请求
bro.get('https://www.baidu.com')
sleep(3)
# 让百度根据指定 搜索词进行搜索
text = bro.find_element_by_id('kw') # 定位文本框
text.send_keys('abc') # 向文本框录入指定内容
sleep(1)
button = bro.find_element_by_id('su')
button.click() # 表示点击操作
sleep(3)

bro.quit()# 退出浏览器

phantomJs(不推荐使用,官网已经停更了)

  • 下载
1
http://phantomjs.org/download.html

PhantomJS是一款无界面的浏览器,其自动化操作流程和上述操作谷歌浏览器是一致的。由于是无界面的,为了能够展示自动化操作流程,PhantomJS为用户提供了一个截屏的功能,使用save_screenshot函数实现。
代码演示:

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
#!/usr/bin/env python
#-*- encoding:utf-8 -*-

from selenium import webdriver
import time

# phantomjs路径
path = r'/Users/almost/Downloads/phantomjs-2.1.1-macosx/bin/phantomjs'
browser = webdriver.PhantomJS(path)

# 打开百度
url = 'http://www.baidu.com/'
browser.get(url)

time.sleep(3)

browser.save_screenshot(r'phantomjs\baidu.png')

# 查找input输入框
my_input = browser.find_element_by_id('kw')
# 往框里面写文字
my_input.send_keys('美女')
time.sleep(3)
#截屏
browser.save_screenshot(r'phantomjs\meinv.png')

# 查找搜索按钮
button = browser.find_elements_by_class_name('s_btn')[0]
button.click()

time.sleep(3)

browser.save_screenshot(r'phantomjs\show.png')

time.sleep(3)

browser.quit()

重点

selenium+phantomjs 就是爬虫终极解决方案:有些网站上的内容信息是通过动态加载js形成的,所以使用普通爬虫程序无法回去动态加载的js内容。例如豆瓣电影中的电影信息是通过下拉操作动态加载更多的电影信息。

综合操作:需求是尽可能多的爬取豆瓣网中的电影信息

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
from selenium import webdriver
from time import sleep
import time

if __name__ == '__main__':
url = 'https://movie.douban.com/typerank?type_name=%E6%81%90%E6%80%96&type=20&interval_id=100:90&action='
# 发起请求前,可以让url表示的页面动态加载出更多的数据
path = r'你的phantomjs路径'
# 创建无界面的浏览器对象
bro = webdriver.PhantomJS(path)
# 发起url请求
bro.get(url)
time.sleep(3)
# 截图
bro.save_screenshot('1.png')

# 执行js代码(让滚动条向下偏移n个像素(作用:动态加载了更多的电影信息))
js = 'window.scrollTo(0,document.body.scrollHeight)'
bro.execute_script(js) # 该函数可以执行一组字符串形式的js代码
time.sleep(2)

bro.execute_script(js) # 该函数可以执行一组字符串形式的js代码
time.sleep(2)
bro.save_screenshot('2.png')
time.sleep(2)
# 使用爬虫程序爬去当前url中的内容
html_source = bro.page_source # 该属性可以获取当前浏览器的当前页的源码(html)
with open('./source.html', 'w', encoding='utf-8') as fp:
fp.write(html_source)
bro.quit()

谷歌无头浏览器

由于PhantomJs最近已经停止了更新和维护,所以推荐大家可以使用谷歌的无头浏览器,是一款无界面的谷歌浏览器。

  • 代码展示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time

# 创建一个参数对象,用来控制chrome以无界面模式打开
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
# 驱动路径
path = r'你的路径chromedriver.exe'

# 创建浏览器对象
browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)

# 上网
url = 'http://www.baidu.com/'
browser.get(url)
time.sleep(3)

browser.save_screenshot('baidu.png')

browser.quit()

Py007-01-11bs4使用

BeautifulSoup解析

python独有

环境安装

  • 需要将pip源设置为国内源,阿里源、豆瓣源、网易源等
    • windows
      (1)打开文件资源管理器(文件夹地址栏中)
      (2)地址栏上面输入 %appdata%
      (3)在这里面新建一个文件夹 pip
      (4)在pip文件夹里面新建一个文件叫做 pip.ini ,内容写如下即可
      [global]
      timeout = 6000
      index-url = https://mirrors.aliyun.com/pypi/simple/
      trusted-host = mirrors.aliyun.com
    • linux
      (1)cd ~
      (2)mkdir ~/.pip
      (3)vi ~/.pip/pip.conf
      (4)编辑内容,和windows一模一样
  • 需要安装:pip install bs4
    bs4在使用时候需要一个第三方库,把这个库也安装一下
    pip install lxml

基础使用

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
使用流程:       
- 导包:from bs4 import BeautifulSoup
- 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容
(1)转化本地文件:
- soup = BeautifulSoup(open('本地文件'), 'lxml')
(2)转化网络文件:
- soup = BeautifulSoup('字符串类型或者字节类型', 'lxml')
(3)打印soup对象显示内容为html文件中的内容

基础巩固:
(1)根据标签名查找
- soup.a 只能找到第一个符合要求的标签
(2)获取属性
- soup.a.attrs 获取a所有的属性和属性值,返回一个字典
- soup.a.attrs['href'] 获取href属性
- soup.a['href'] 也可简写为这种形式
(3)获取内容
- soup.a.string
- soup.a.text
- soup.a.get_text()
【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
(4)find:找到第一个符合要求的标签
- soup.find('a') 找到第一个符合要求的
- soup.find('a', title="xxx")
- soup.find('a', alt="xxx")
- soup.find('a', class_="xxx")
- soup.find('a', id="xxx")
(5)find_all:找到所有符合要求的标签
- soup.find_all('a')
- soup.find_all(['a','b']) 找到所有的a和b标签
- soup.find_all('a', limit=2) 限制前两个
(6)根据选择器选择指定的内容
select:soup.select('#feng')
- 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
- 层级选择器:
div .dudu #lala .meme .xixi 下面好多级
div > p > a > .lala 只能是下面一级
【注意】select选择器返回永远是列表,需要通过下标提取指定的对象

需求

使用bs4实现将诗词名句网站中三国演义小说的每一章的内容爬去到本地磁盘进行存储 http://www.shicimingju.com/book/sanguoyanyi.html

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
import requests
from bs4 import BeautifulSoup

headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}
def parse_content(url):
#获取标题正文页数据
page_text = requests.get(url,headers=headers).text
soup = BeautifulSoup(page_text,'lxml')
#解析获得标签
ele = soup.find('div',class_='chapter_content')
content = ele.text #获取标签中的数据值
return content

if __name__ == "__main__":
url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
reponse = requests.get(url=url,headers=headers)
page_text = reponse.text

#创建soup对象
soup = BeautifulSoup(page_text,'lxml')
#解析数据
a_eles = soup.select('.book-mulu > ul > li > a')
print(a_eles)
cap = 1
fp = open('./sanguo.txt', 'w')

for ele in a_eles:
print('开始下载第%d章节'%cap)
cap+=1
title = ele.string
content_url = 'http://www.shicimingju.com'+ele['href']
content = parse_content(content_url)

fp.write(title+":"+content+'\n\n\n')
print('结束下载第%d章节'%cap)

Py007-01-10xpath使用

xpath在爬虫中使用流程

1
2
3
4
5
6
7
- 1.下载: pip install lxml
- 2.导包: from lxml import etree
- 3.创建etree对象 进行指定数据的解析
- 本地 etree = etree.parse('本地文件路径')
etree.xpath('xpath表达式')
- 网络 etree = etree.HTML('网络请求到的页面数据')
etree.xpath('xpath表达式')

常用xpath表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

属性定位:
#找到class属性值为song的div标签
//div[@class="song"]
层级&索引定位:
#找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a
//div[@class="tang"]/ul/li[2]/a
逻辑运算:
#找到href属性值为空且class属性值为du的a标签
//a[@href="" and @class="du"]
模糊匹配:
# 当前所有div 同时class属性包含了 ng
# 如 <div class="song"> 和<div class="tang"> 都会被匹配
//div[contains(@class, "ng")]
# 当前所有div 的class属性 以 ta开头的
//div[starts-with(@class, "ta")]
取文本:
# /表示获取某个标签下的文本内容
//div[@class="song"]/p[1]/text()
# //表示获取某个标签下的文本内容和所有子标签下的文本内容
//div[@class="tang"]//text()
取属性:
//div[@class="tang"]//li[2]/a/@href

xpath插件

我们不可能每次都把html下载到本地 来写xpath表达式

xpath插件可以直接作用在浏览器当中来校验xpath表达式

  • 安装
1
2
3
4
5
6
1. 更多工具
2. 扩展程序
3. 开启开发者模式
4. XPath Helper 找到它 安装
5. 快捷键
- 开启和关闭 xpath插件 ctrl + shift + x

段子网的段子内容和标题解析

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
#!/usr/bin/env python
#-*- encoding:utf-8 -*-

from lxml import etree
import requests
# 1. 指定url
url='https://ishuo.cn/joke'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
}
# 2 发请求
response=requests.get(url,headers=headers).text
# 3 获取页面内容
page_text = response.text
#4 解析
tree=etree.HTML(page_text)
# 获取所有li
li_list=tree.xpath('//div[@id="list"]/ul/li')
# 注意 Element类型对象可以继续用xpath函数,该对象表示的局部内容进行指定内容的解析

fp = open('./duanzi.txt','w',encoding='utf-8')

for li in li_list:
content = li.xpath('./div[@class="content"]/text()')[0]
title = li.xpath('./div[@class="info"]/a/text()')[0]
# 5 持久化
fp.write(title+':'+content+'\n\n')
print('写入成功')

Py007-01-09爬虫之数据解析以及re操作

数据解析

  1. 指定url
  2. 发请求
  3. 获取页面数据
  4. 数据解析
  5. 进行持久化

三种数据解析方式

  • 正则
  • bs4
  • xpath

正则re模块

用法回顾

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
单字符:
. : 除换行以外所有字符
[] :[aoe] [a-w] 匹配集合中任意一个字符
\d :数字 [0-9]
\D : 非数字
\w :数字、字母、下划线、中文
\W : 非\w
\s :所有的空白字符包,括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S : 非空白
数量修饰:
* : 任意多次 >=0
+ : 至少1次 >=1
? : 可有可无 0次或者1次
{m} :固定m次 hello{3,}
{m,} :至少m次
{m,n} :m-n次
边界:
$ : 以某某结尾
^ : 以某某开头
分组:
(ab)
贪婪模式: .*
非贪婪(惰性)模式: .*?

re.I : 忽略大小写
re.M :多行匹配
re.S :单行匹配

re.sub(正则表达式, 替换内容, 字符串)

代码示例

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/usr/bin/env python
#-*- encoding:utf-8 -*-
import re

# 提取python
key = 'javapythonc++php'
res =re.findall('python',key)
print(res) # ['python']

# 提取hello world
key = '<html><h1>hello world</h1></html>'
res =re.findall('<h1>(hello world)</h1>',key)
print(res) # ['hello world']

# 提取170
key = '我喜欢身高170的女孩'
res =re.findall('\d+',key)
print(res) # ['170']

# 提取 http 和 https
key = 'http://www.baidu.com and https://boob.com'
res =re.findall('https?',key)
res2 = re.findall('https{0,1}',key)
print(res,res2) # ['http', 'https'] ['http', 'https']

# 提取hit.
key = 'bobo@hit.edu.com'
res =re.findall('h.*\.',key) # ['hit.edu.']
print(res) # ['hit.edu.'] 贪婪模式(默认)尽可能多的提取数据
res2 =re.findall('h.*?\.',key)
print(res2) # ['hit.']

# 提取 sas saas
key = 'saas and sas and saaas'
res =re.findall('sa{1,2}s',key)
print(res) # ['saas', 'sas']


# 提取i开头的行
'''
re.S 基于单行
re.M 基于多行
'''
key = '''fall in love with you
i love you very much
i love she
i love her'''
res = re.findall('^i.*',key,re.M)
print(res)
# ['i love you very much', 'i love she', 'i love her']

# 匹配所有全部行
key = '''<div>静夜思
窗前明月光
疑是地上霜
举头望明月
低头思故乡
</div>'''

'''
此时key是多行
但是匹配的时候想要当作一行处理
'''
res = re.findall('<div>.*</div>',key,re.S)
print(res)
# ['<div>静夜思\n窗前明月光\n疑是地上霜\n举头望明月\n低头思故乡\n</div>']

需求:用正则对糗百的(糗图)图片数据进行解析和下载

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
49
50
#!/usr/bin/env python
#-*- encoding:utf-8 -*-

import requests
import re
import os
#1指定url
url = 'https://www.qiushibaike.com/pic/'

#自定义请求头信息
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}

#2发请求
response = requests.get(url=url,headers=headers)

#3获取页面数据
page_text = response.text

#4数据解析
'''
<div class="thumb">
<a href="/article/121304175" target="_blank">
<img src="//pic.qiushibaike.com/system/pictures/12130/121304175/medium/7RG0RFB30I0KWFMI.jpg" alt="要把下面烫卷">
</a>
</div>
'''
# 该列表中存储的就是当前页面源码中所有图片的url
img_arr = re.findall('<div class="thumb">.*?<img src="(.*?)".*?>.*?</div>',page_text,re.S)

# 创建存储图片数据的文件夹
if not os.path.exists('./imgs'):
os.mkdir('imgs')
'''
# 发现是不含协议部分的
['//pic.qiushibaike.com/system/pictures/12130/121304185/medium/6X8SQWYH0BBUO1KV.jpg',....]'''
# 添加上协议
for url in img_arr:
img_url = 'https:' + url
# 持久化存储:存储的是图片的数据而不是url
# 获取图片二进制数据值
img_data = requests.get(url=img_url,headers=headers).content

# 将 6X8SQWYH0BBUO1KV.jpg 当做图片的名字
img_name = url.split('/')[-1]
img_path = 'imgs/' + img_name
with open(img_path,'wb') as f:
f.write(img_data)
print(img_name+'写入成功')

Py007-01-07requests高级操作cookie和代理

requests模块高级:

cookie:

基于用户的用户数据

  • 需求:爬取张三用户的豆瓣网的个人主页页面数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#问题:没有获取个人主页的页面数据
#原因:爬虫程序没有严格遵从浏览器的请求流程
import requests

#1.指定url
url = 'https://www.douban.com/people/185687620/'
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}
#2.发起请求
response = requests.get(url=url,headers=headers)

#3.获取页面数据
page_text = response.text

#4.持久化存储
with open('./douban.html','w',encoding='utf-8') as fp:
fp.write(page_text)

无法访问对应页码的成因

1
结果发现,写入到文件中的数据,不是张三个人页面的数据,而是豆瓣登陆的首页面,why?首先我们来回顾下cookie的相关概念及作用:
  • cookie概念:当用户通过浏览器首次访问一个域名时,访问的web服务器会给客户端发送数据,以保持web服务器与客户端之间的状态保持,这些数据就是cookie。

  • cookie作用:我们在浏览器中,经常涉及到数据的交换,比如你登录邮箱,登录一个页面。我们经常会在此时设置30天内记住我,或者自动登录选项。那么它们是怎么记录信息的呢,答案就是今天的主角cookie了,Cookie是由HTTP服务器设置的,保存在浏览器中,但HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。就像我们去超市买东西,没有积分卡的情况下,我们买完东西之后,超市没有我们的任何消费信息,但我们办了积分卡之后,超市就有了我们的消费信息。cookie就像是积分卡,可以保存积分,商品就是我们的信息,超市的系统就像服务器后台,http协议就是交易的过程。

  • 经过cookie的相关介绍,其实你已经知道了为什么上述案例中爬取到的不是张三个人信息页,而是登录页面。那应该如何抓取到张三的个人信息页呢?

思路:

  1. 我们需要使用爬虫程序对豆瓣的登录时的请求进行一次抓取,获取请求中的cookie数据
  2. 在使用个人信息页的url进行请求时,该请求需要携带 1 中的cookie,只有携带了cookie后,服务器才可识别这次请求的用户信息,方可响应回指定的用户信息页数据

cookie作用:服务器端使用cookie来记录客户端的状态信息。

实现流程:

  1. 执行登录操作(获取cookie)
  2. 在发起个人主页请求时,需要将cookie携带到该请求中

注意:session对象:发送请求(会将cookie对象进行自动存储)

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
import requests

session = requests.session()
#1.发起登录请求:将cookie获取,切存储到session对象中
login_url = 'https://accounts.douban.com/login'
data = {
"source": "None",
"redir": "https://www.douban.com/people/185687620/",
"form_email": "15027900535",
"form_password": "bobo@15027900535",
"login": "登录",
}
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}
#使用session发起post请求
login_response = session.post(url=login_url,data=data,headers=headers)

#2.对个人主页发起请求(session(cookie)),获取响应页面数据
url = 'https://www.douban.com/people/185687620/'
response = session.get(url=url,headers=headers)
page_text = response.text

with open('./douban110.html','w',encoding='utf-8') as fp:
fp.write(page_text)

代理

基于requests模块的代理操作

  • 什么是代理

代理就是第三方代替本体处理相关事务。例如:生活中的代理:代购,中介,微商……

  • 爬虫中为什么需要使用代理

一些网站会有相应的反爬虫措施,例如很多网站会检测某一段时间某个IP的访问次数,如果访问频率太快以至于看起来不像正常访客,它可能就会会禁止这个IP的访问。所以我们需要设置一些代理IP,每隔一段时间换一个代理IP,就算IP被禁止,依然可以换个IP继续爬取。

  • 代理的分类:

正向代理:代理客户端获取数据。正向代理是为了保护客户端防止被追究责任。

反向代理:代理服务器提供数据。反向代理是为了保护服务器或负责负载均衡。

  • 免费代理ip提供网站
  1. http://www.goubanjia.com/ (推荐)

  2. 西祠代理

  3. 快代理

代理实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests

url = 'https://www.baidu.com/s?ie=utf-8&wd=ip'

headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
}

#将代理ip封装到字典
proxy = {
'http':'77.73.69.120:3128',
# 'https':'xxxx' 也可以是https
}
#更换网路IP
response = requests.get(url=url,proxies=proxy,headers=headers)

with open('./daili.html','w',encoding='utf-8') as fp:
fp.write(response.text)

注意点

  • 请求的协议要与代理ip的协议统一

Py007-01-06requests综合实战

综合项目实战

  • 需求:爬取搜狗知乎某一个词条对应一定范围页码表示的页面数据

分析网址

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
# 原始url
baseUrl = 'https://zhihu.sogou.com/zhihu?query=%E9%92%89%E9%92%89&ie=utf8&w=&oq=dd&ri=1&sourceid=sugg&stj=1%3B0%3B0%3B0&stj2=0&stj0=1&stj1=0&hp=0&hp1=&sut=1909&sst0=1543542920653&lkt=3%2C1543542918642%2C1543542920549'

# 移除get参数的url
url2 = 'https://zhihu.sogou.com/zhihu'

# get 参数 = 'query=%E9%92%89%E9%92%89&ie=utf8&w=&oq=dd&ri=1&sourceid=sugg&stj=1%3B0%3B0%3B0&stj2=0&stj0=1&stj1=0&hp=0&hp1=&sut=1909&sst0=1543542920653&lkt=3%2C1543542918642%2C1543542920549'
params = {
query: 钉钉
ie: utf8
w:
oq: dd
ri: 1
sourceid: sugg
stj: 1;0;0;0
stj2: 0
stj0: 1
stj1: 0
hp: 0
hp1:
sut: 1909
sst0: 1543542920653
lkt: 3,1543542918642,1543542920549
}
# 移除无用参数
params = {
'query': 搜索词,
'page': 页码,
'ie': 'utf-8'
}

代码实现

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
# 前三页页面数据(1,2,3)
import requests
import os

# 创建一个文件夹
if not os.path.exists('./pages'):
os.mkdir('./pages')

word = input('enter a word:')

# 动态指定页码的范围
start_pageNum = int(input('enter a start pageNum:'))
end_pageNum = int(input('enter a end pageNum:'))
# 自定义请求头信息
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}
# 1.指定url:设计成一个具有通用的url

# 请求url
url = 'https://zhihu.sogou.com/zhihu'
for page in range(start_pageNum, end_pageNum + 1):
param = {
'query': word,
'page': page,
'ie': 'utf-8'
}
response = requests.get(url=url, params=param, headers=headers)

# 获取响应中的页面数据(指定页码(page))
page_text = response.text

# 进行持久化存储
fileName = word + str(page) + '.html'
filePath = 'pages/' + fileName
with open(filePath, 'w', encoding='utf-8') as fp:
fp.write(page_text)
print('第%d页数据写入成功' % page)

Py007-01-05requests模块之post和ajax

requests模块之post请求

简单需求,登录豆瓣网,获取成功后的数据

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
import requests

# 1.post请求的url
url = 'https://accounts.douban.com/login'

# 封装post请求参数数据
data = {
"source":'movie',
'redir':'https://movie.douban.com/',
'form_email':'15027900535',
'form_password':'bobo@15027900535',
'login':'登录'
}

#自定义请求头信息
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}
# 2 发起post请求
response = requests.post(url=url,data=data,headers=headers)

# 3 获取响应对象中的页面数据
page_text = response.text

# 4 持久化存储
with open('movie.html','w',encoding='utf-8') as f:
f.write(page_text)

ajax的请求

基于ajax的get请求

抓取豆瓣电影首页-排行榜-某一分类的电影的下拉加载数据

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
import requests

# 原始url
# baseurl = 'https://movie.douban.com/j/chart/top_list?type=24&interval_id=100%3A90&action=&start=40&limit=20'

# 指定url
url = 'https://movie.douban.com/j/chart/top_list'

# 封装ajax中 get参数
params = {
'type':24,
'interval_id':'100:90',
'action':'',
'start':40,
'limit':20
}

# 自定义请求头信息
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}

response = requests.get(url=url,params=params,headers=headers)

print(response.text)

基于ajax的post请求

获取肯德基城市餐厅位置数据

1
2
3
4
5
6
7
8
http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword

参数
cname:
pid:
keyword: 上海
pageIndex: 2
pageSize: 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests

url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'


# 封装ajax中 post参数
data = {
'cname':'',
'pid':'',
'keyword': '上海',
'pageIndex':2,
'pageSize':10
}

#自定义请求头信息
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}

response = requests.post(url=url,data=data,headers=headers)

print(response.text)

结论

  • 如果使用requests发起ajax请求 跟普通get/post请求是一样的
  • 无法通过地址栏请求,通过浏览器开发者工具抓包信息获取请求的url

Py007-01-04requests模块

requests模块

  • 什么是requests模块

requests模块是python中原生的基于网络请求的模块,其主要作用是用来模拟浏览器发起请求。功能强大,用法简洁高效。在爬虫领域中占据着半壁江山的地位。

  • 为什么要使用requests模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> 因为在使用urllib模块的时候,会有诸多不便之处,总结如下:
1 手动处理url编码
2 手动处理post请求参数
3 处理cookie和代理操作繁琐
> cookie
- 创建一个cookiejar对象
- 创建一个handler对象
- 创建一个opener对象

> 代理
- 创建handler对象,代理ip和端口封装到该对象
- 创建opener对象

> 使用requests模块:
- 自动处理url编码
- 自动处理post请求参数
- 大大简化cookie和代理操作
。。。

requests如何使用

1
2
# 安装
pip install requests

使用流程

1
2
3
4
1. 指定url
2. 使用requests模块发请求
3. 获取响应数据
4. 进行持久化存储

通过5个基于requests模块的爬虫项目对该模块进行系统的学习和巩固

  • get
  • post
  • ajax的get
  • ajax的post
  • 综合

get请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

# 指定url
url = 'https://www.sogou.com/'

# 发get请求,get方法会返回请求成功的响应内容
response = requests.get(url=url)

# 获取响应中的数据值:text可以获取响应对象中字符串形式的页面数据
page_data = response.text

print(page_data)

# 持久化存储
with open('./sogou.html','w',encoding='utf-8') as fp:
fp.write(page_data)

requests其他常用属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests

url = 'https://www.sogou.com/'

response = requests.get(url=url)

page_data = response.text
# 获取响应对象中二进制(byte)类型的数据
print(response.content)

# 返回响应的状态码
print(response.status_code)

# 获取响应的头信息(字典的形式)
print(response.headers)

# 获取请求的url
print(response.url)

request携带参数的get请求方式一

获取搜狗搜索结果对应的界面数据爬取

获取周杰伦对应的搜狗页面

1
2
3
4
5
6
7
8
9
10
import requests

url = 'https://www.sogou.com/web?query=周杰伦&ie=utf-8'

response = requests.get(url=url)

page_text = response.text

with open('./zhou.html','w',encoding='utf-8') as f:
f.write(page_text)

此时url里的中文参数无需手动处理(urllib需要手动处理)

request携带参数的get请求方式二

  • params参数(字典的形势传递get参数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

# get请求方式二
url = 'https://www.sogou.com/web'

# 将参数封装到字典里
params = {
'query':'周杰伦',
'ie':'utf-8'
}

response = requests.get(url=url,params=params)

page_text = response.text

print(page_text)

requests之自定义请求头信息

  • headers 请求头参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests

url = 'https://www.sogou.com/'

#自定义请求头信息
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}

# 将参数封装到字典里
params = {
'query':'周杰伦',
'ie':'utf-8'
}

response = requests.get(url=url,params=params,headers=headers)

print(response.text)

Py007-01-03内置urllib模块补充

urllib补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 补充说明:
urlopen函数原型:
urllib.request.urlopen(url, data=None, timeout=<object object at 0x10af327d0>, *, cafile=None, capath=None, cadefault=False, context=None)

在上一节中我们只使用了该函数中的第一个参数url。在日常开发中,我们能用的只有url和data这两个参数。

url参数:指定向哪个url发起请求
data参数:可以将post请求中携带的参数封装成字典的形式传递给该参数

urlopen函数返回的响应对象,相关函数调用介绍:
response.headers():获取响应头信息
response.getcode():获取响应状态码
response.geturl():获取请求的url
response.read():获取响应中的数据值(字节类型)

二进制数据的爬取

爬取网络上的某张图片数据,且存储到磁盘

  • 方法 1:
1
2
3
4
5
6
7
8
9
10
11
12
import urllib.request
import urllib.parse

#1.指定url
url = 'https://pic.qiushibaike.com/system/pictures/12112/121121212/medium/ZOAND29U4NKNEWEF.jpg'
#2.发起请求:使用urlopen函数发起请求,该函数返回一个响应对象
response = urllib.request.urlopen(url=url)
#3.获取响应对象中的图片二进制类型的数据
img_data = response.read()
#4.持久化存储:将爬取的图片写入本地进行保存
with open('./tupian.png','wb') as fp:
fp.write(img_data)
  • 方法 2:
1
2
3
4
5
6
7
import urllib.request
import urllib.parse

url = 'https://pic.qiushibaike.com/system/pictures/12112/121121212/medium/ZOAND29U4NKNEWEF.jpg'
# 函数原型:urllib.request.urlretrieve(url, filename=None)
# 作用:对url发起请求,且将响应中的数据值写入磁盘进行存储
urllib.request.urlretrieve(url=url,filename='./img.png')

url的特性

url必须为ASCII编码的数据值。所以我们在爬虫代码中编写url时,如果url中存在非ASCII编码的数据值,则必须对其进行ASCII编码后,该url方可被使用。

案例:爬取使用搜狗根据指定词条搜索到的页面数据(例如爬取词条为‘周杰伦’的页面数据)

1
2
3
4
5
6
7
8
import urllib.request
import urllib.parse
url = 'https://www.sogou.com/web?query=周杰伦'
response = urllib.request.urlopen(url=url)
data = response.read()
with open('./周杰伦.html','wb') as fp:
fp.write(data)
print('写入文件完毕')

【注意】上述代码中url存在非ascii编码的数据,则该url无效。如果对其发起请求,则会报如下错误:

1
UnicodeEncodeError: 'ascii' codec can't encode characters in position 15-17: ordinal not in range

所以必须对url中的非ascii的数据进行ascii的编码,则该url方可被发起请求:

  • 方法 1:使用quote函数
1
2
3
4
5
6
7
8
9
10
11
12
import urllib.request
import urllib.parse
url = 'https://www.sogou.com/web?query=%s'
#对url中的非ascii进行编码.quote函数可以对非ascii的数据值进行ascii的编码
word = urllib.parse.quote('周杰伦')
#将编码后的数据值拼接回url中
url = format(url%word)
response = urllib.request.urlopen(url=url)
data = response.read()
with open('./周杰伦.html','wb') as fp:
fp.write(data)
print('写入文件完毕')
  • 方法2: 使用urlencode函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import urllib.request
import urllib.parse
url = 'https://www.sogou.com/web?'
#将get请求中url携带的参数封装至字典中
param = {
'query':'周杰伦'
}
#对url中的非ascii进行编码
param = urllib.parse.urlencode(param)
#将编码后的数据值拼接回url中
url += param
response = urllib.request.urlopen(url=url)
data = response.read()
with open('./周杰伦1.html','wb') as fp:
fp.write(data)
print('写入文件完毕')

通过自定义请求对象,用于伪装爬虫程序请求的身份。

之前在讲解http常用请求头信息时,我们讲解过User-Agent参数,简称为UA,该参数的作用是用于表明本次请求载体的身份标识。如果我们通过浏览器发起的请求,则该请求的载体为当前浏览器,则UA参数的值表明的是当前浏览器的身份标识表示的一串数据。如果我们使用爬虫程序发起的一个请求,则该请求的载体为爬虫程序,那么该请求的UA为爬虫程序的身份标识表示的一串数据。有些网站会通过辨别请求的UA来判别该请求的载体是否为爬虫程序,如果为爬虫程序,则不会给该请求返回响应,那么我们的爬虫程序则也无法通过请求爬取到该网站中的数据值,这也是反爬虫的一种初级技术手段。那么为了防止该问题的出现,则我们可以给爬虫程序的UA进行伪装,伪装成某款浏览器的身份标识。

上述案例中,我们是通过request模块中的urlopen发起的请求,该请求对象为urllib中内置的默认请求对象,我们无法对其进行UA进行更改操作。urllib还为我们提供了一种自定义请求对象的方式,我们可以通过自定义请求对象的方式,给该请求对象中的UA进行伪装(更改)操作。

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
import urllib.request
import urllib.parse
url = 'https://www.sogou.com/web?'
#将get请求中url携带的参数封装至字典中
param = {
'query':'周杰伦'
}
#对url中的非ascii进行编码
param = urllib.parse.urlencode(param)
#将编码后的数据值拼接回url中
url += param

#封装自定义的请求头信息的字典:
#将浏览器的UA数据获取,封装到一个字典中。该UA值可以通过抓包工具或者浏览器自带的开发者工具中获取某请求,从中获取UA的值
#注意:在headers字典中可以封装任意的请求头信息
headers={
'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
#自定义请求对象,可以在该请求对象中添加自定义的请求头信息
request = urllib.request.Request(url=url,headers=headers)
#使用自定义请求对象发起请求
response = urllib.request.urlopen(request)
data = response.read()
with open('./周杰伦.html','wb') as fp:
fp.write(data)
print('写入文件完毕')

携带参数的post请求

案例:百度翻译发起post请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import urllib.request
import urllib.parse
#通过抓包工具抓取post请求的url
post_url='https://fanyi.baidu.com/sug'
#封装post请求参数
data={
"kw":"dog"
}
data=urllib.parse.urlencode(data)
#自定义请求头信息字典
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"
}

#自定义请求对象,然后将封装好的post请求参数赋值给Requst方法的data参数。
#data参数:用来存储post请求的参数
request=urllib.request.Request(post_url,data=data.encode(),headers=headers)
#自定义的请求对象中的参数(data必须为bytes类型)
response=urllib.request.urlopen(request)
response.read()

urllib模块的高级操作

  1. 代理
  • 什么是代理:代理就是第三方代替本体处理相关事务。例如:生活中的代理:代购,中介,微商……

  • 爬虫中为什么需要使用代理?

    一些网站会有相应的反爬虫措施,例如很多网站会检测某一段时间某个IP的访问次数,如果访问频率太快以至于看起来不像正常访客,它可能就会会禁止这个IP的访问。所以我们需要设置一些代理IP,每隔一段时间换一个代理IP,就算IP被禁止,依然可以换个IP继续爬取。

  • 代理的分类:

正向代理:代理客户端获取数据。正向代理是为了保护客户端防止被追究责任。

反向代理:代理服务器提供数据。反向代理是为了保护服务器或负责负载均衡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import urllib.request
import urllib.parse

#1.创建处理器对象,在其内部封装代理ip和端口
handler=urllib.request.ProxyHandler(proxies={'http':'95.172.58.224:52608'})
#2.创建opener对象,然后使用该对象发起一个请求
opener=urllib.request.build_opener(handler)

url='http://www.baidu.com/s?ie=UTF-8&wd=ip'

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
}

request = urllib.request.Request(url, headers=headers)

#使用opener对象发起请求,该请求对应的ip即为我们设置的代理ip
response = opener.open(request)

with open('./daili.html','wb') as fp:
fp.write(response.read())

2.cookie

引言:有些时候,我们在使用爬虫程序去爬取一些用户相关信息的数据(爬取张三“人人网”个人主页数据)时,如果使用之前requests模块常规操作时,往往达不到我们想要的目的,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import urllib.request
import urllib.parse
#指定url
url = 'http://www.renren.com/289676607/profile'
#自定义请求头信息
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}
#自定义请求对象
request = urllib.request.Request(url=url,headers=headers)
#发起请求
response = urllib.request.urlopen(request)

with open('./renren.html','w') as fp:
fp.write(response.read().decode())

【注意】上述代码中,我们爬取到的是登录首页面,而不是张三的个人主页也面。why?首先我们来回顾下cookie的相关概念及作用

- cookie概念:当用户通过浏览器首次访问一个域名时,访问的web服务器会给客户端发送数据,以保持web服务器与客户端之间的状态保持,这些数据就是cookie。

- cookie作用:我们在浏览器中,经常涉及到数据的交换,比如你登录邮箱,登录一个页面。我们经常会在此时设置30天内记住我,或者自动登录选项。那么它们是怎么记录信息的呢,答案就是今天的主角cookie了,Cookie是由HTTP服务器设置的,保存在浏览器中,但HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。就像我们去超市买东西,没有积分卡的情况下,我们买完东西之后,超市没有我们的任何消费信息,但我们办了积分卡之后,超市就有了我们的消费信息。cookie就像是积分卡,可以保存积分,商品就是我们的信息,超市的系统就像服务器后台,http协议就是交易的过程。 

- 经过cookie的相关介绍,其实你已经知道了为什么上述案例中爬取到的不是张三个人信息页,而是登录页面。那应该如何抓取到张三的个人信息页呢?

  思路:

    1.我们需要使用爬虫程序对人人网的登录时的请求进行一次抓取,获取请求中的cookie数据

    2.在使用个人信息页的url进行请求时,该请求需要携带 1 中的cookie,只有携带了cookie后,服务器才可识别这次请求的用户信息,方可响应回指定的用户信息页数据

1
2
3
4
5
6
7
8
9
10
11
12
cookiejar对象:
- 作用:自动保存请求中的cookie数据信息
- 注意:必须和handler和opener一起使用
cookiejar使用流程:
- 创建一个cookiejar对象
import http.cookiejar
cj = http.cookiejar.CookieJar()
- 通过cookiejar创建一个handler
handler = urllib.request.HTTPCookieProcessor(cj)
- 根据handler创建一个opener
opener = urllib.request.build_opener(handler)
- 使用opener.open方法去发送请求,且将响应中的cookie存储到openner对象中,后续的请求如果使用openner发起,则请求中就会携带了cookie

使用cookiejar实现爬取人人网个人主页页面数据:

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
#使用cookiejar实现人人网的登陆
import urllib.request
import urllib.parse
import http.cookiejar
cj = http.cookiejar.CookieJar() #请求中的cookie会自动存储到cj对象中
#创建处理器对象(携带cookiejar对象的)
handler=urllib.request.HTTPCookieProcessor(cj)
#创建opener对象 (携带cookiejar对象)
opener=urllib.request.build_opener(handler)

#要让cookiejar获取请求中的cookie数据值
url='http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=201873958471'
#自定义一个请求对象,让该对象作为opener的open函数中的参数
data={
"email":"www.zhangbowudi@qq.com",
"icode":"",
"origURL":"http://www.renren.com/home",
"domain":"renren.com",
"key_id":"1",
"captcha_type":"web_login",
"password":"40dc65b82edd06d064b54a0fc6d202d8a58c4cb3d2942062f0f7dd128511fb9b",
"rkey":"41b44b0d062d3ca23119bc8b58983104",

'f':"https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DpPKf2680yRLbbZMVdntJpyPGwrSk2BtpKlEaAuKFTsW%26wd%3D%26eqid%3Deee20f380002988c000000025b7cbb80"
}
data=urllib.parse.urlencode(data).encode()
request=urllib.request.Request(url,data=data)
opener.open(request)

#获取当前用户的二级子页面
s_url='http://www.renren.com/289676607/profile'
#该次请求中就携带了cookie
resonse=opener.open(s_url)

with open('./renren.html','wb') as fp:
fp.write(resonse.read())