常用爬虫库

对于请求、响应、http等知识还处于迷迷惑惑的状态,理论知识以后要慢慢啃

这次要学的爬虫框架:requests,

1.requests

这里先说一下requests和urllib的区别

1)构建参数:在构建请求参数时,第一种需要将请求参数使用urllib库的urlencode方法进行编码预处理,非常麻烦

2)请求方法:发送get请求时,第一种使用的urllib库的urlopen方法打开一个url地址,而第二种直接使用requests库的get方法,与http请求方式是对应的,更加直接、易懂

3)请求数据:第一种按照url格式去拼接一个url字符串,显然非常麻烦,第二种按顺序将get请求的url和参数写好就可以了

4)处理响应:第一种处理消息头部、响应状态码和响应正文时分别使用.info()、.getcode()、.read()方法,第二种使用.headers、.status_code、.text方法,方法名称与功能本身相对应,更方便理解、学习和使用

5)连接方式:看一下返回数据的头信息的“connection”,使用urllib库时,”connection”:”close”,说明每次请求结束关掉socket通道,而使用requests库使用了urllib3,多次请求重复使用一个socket,”connection”:”keep-alive”,说明多次请求使用一个连接,消耗更少的资源

6)编码方式:requests库的编码方式Accept-Encoding更全,在此不做举例

1.1 get和post请求

  • 使用requests发送get请求将百度搜索的页面源码爬回来
1
2
3
4
import requests
r = requests.get("https://www.baidu.com")#返回结果:<Response [200]>一个response对象
r = r.text
print(r)
  • 发送post请求,也就是直接调用post方法
1
r = requests.post(http://httpbin.org/post,data={'key':'data'})

1.2通过URL传递参数

URL不仅仅是一个网址,我们再访问url的时候经常会带上一些查询的字符串,即请求参数

也就是在使用get请求的时候所说的将信息放在车顶上。requests允许通过字典或字符串来传参

1
2
3
4
payload = {'key1':'value1','key2':'value2'}
r= requests.get("https://httpbin.org/get", params = payolad)
print(r.url)
输出结果:http://www.example.com?key1=value1&key2=value2

1.3设置超时

请求时设置等待时间,以免等待太久,在请求时给参数timeout一个数字,单位是秒。超出时间未返回结果即报错

1
2
3
r=requests.get("https://github.com",timeout=0.01)
requests.exceptions.ConnectTimeout:
HTTPSConnectionPool(host='github.com', port=443): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x00000000032D7310>, 'Connection to github.com timed out. (connect timeout=0.01)'))

1.4设置请求头

请求不到数据,而又没加请求头,在确认请求正确的前提下,多半是爬虫的身份被识破了。

可以通过添加请求头,来伪装浏览器发送请求

1
2
3
4
url = "https://movie.douban.com/top250"
header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0'}
r= requests.get(url,headers = header)
print(r.text)

对比urllib的使用,语句确实要简单很多

1.5 post请求

1
2
3
payload = {'key1':'value1','key2':'value2'}
r = requests.post('http://httpbin.org/post',data = payload)
print(r.text)

请求成功并返回结果,form字段中字典和刚才传入的字典{‘key1’:’value1’,’key2’:’value2’}

1.6 返回状态码

1
2
3
r = requests.get('http://httpbin.org/get')
r.status_code
在请求成功的情况下,返回的状态码是200

1.7 设置代理IP

访问网络用的协议是TCP/IP,也就是访问网络必须要有一个IP地址,每个人的Ip地址是唯一的。

代理IP是指本机先访问代理服务器,然后代理服务器通过代理IP去访问指定网页。这样网页留下的记录就是代理IP。

一般的服务器在发现某个IP访问频率过高的时候会采取封禁操作,所以代理IP就成了防止爬虫被BAN的利器。

1
2
proxies = {'http':'http://10.10.1.10:3128'}
requests.get{'http://httpbin.org/ip',proxies = proxies}

免费的ip代理大多无法正常使用,想要使用代理可能还是要vip

1.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
# -*- coding: utf-8 -*-

import requests

userAgent = r"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"
header = {
# "origin": "https://passport.mafengwo.cn",
"Referer": "http://172.16.200.13/",
'User-Agent': userAgent,
}


def mafengwoLogin(account, password):
# 马蜂窝模仿 登录
print("开始模拟登录校园网")

postUrl = "https://172.16.200.13/"
postData = {
"DDDDD": account,
"upass": password,
"0MKKey": "123",
}
responseRes = requests.post(postUrl, data=postData, headers=header,verify=False)
# 无论是否登录成功,状态码一般都是 statusCode = 200
print(f"statusCode = {responseRes.status_code}")
print(f"text = {responseRes.text}")


if __name__ == "__main__":
# 从返回结果来看,有登录成功
mafengwoLogin("2019171092", "125231")

2.BeautifulSoup

对源码进行解析和数据提取

2.1 定义

将html文档转换成树形结构,每个节点都是python对象。可以将节点对象归纳为四种:

  • Tag

  • NavigableString

  • BeautifulSoup
  • Comment

2.2 语法

1
2
3
4
5
6
7
from bs4 import BeautifulSoup
url = "https://movie.douban.com/top250"
header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0'}
r= requests.get(url,headers = header)
html = r.text

bs = BeautifulSoup(html,”html.parser”)##使用html解析器对传入html文件进行解析
  • print(bs):文档整体就是BeautifulSoup
  • print(bs.tittle):Tag及其内容,获取找到的第一个内容
  • print(bs.tittle.string):NavigableString,标签里的内容(字符串)
  • print (bs.a.attrs):获取标签中的全部属性,以键值对保存在字典
  • print(bs.a.string):Comment是一个特殊的NavigableString,输出内容不包含注释

2.3遍历文档树

  • .contents:获取tag的所有子节点,返回一个list

    print(bs.head.contents);获取某一子节点print(bs.head.content[i])

  • .children:获取tag的所有子节点,返回一个生成器

    for child in bs.head.children:

    print(child)

  • .descendants:获取tag的所有子孙节点,返回一个生成器

  • .strings:获取tag的所有子孙节点中的内容
  • stripped_strings:与strings用法一致,可以消去空白
  • .parent:获取tag的所有父节点
  • .parents:递归得到父辈元素的所有节点,返回生成器
  • .previous_sibling:获取当前tag的上一个节点
  • .next_sibling:获取当前tag的下一个节点
  • .previous_siblings:获取当前tag的上面所有兄弟节点,返回生成器
  • .next_siblings:获取当前tag的上面所有兄弟节点,返回生成器
  • .previous_element:解析过程中上一个解析对象
  • .next_element:解析过程中下一个解析对象
  • .previous_elements:向前访问文档的解析内容,返回生成器
  • .next_elements:向后访问文档的解析内容,返回生成器
  • .has_attr:判断tag是否包含属性

2.4 元素定位

2.4.1find_all()

  1. 字符串过滤,查找所有与字符串匹配的文档,返回列表

t_list = bs.find_all(“a”) 结果:a标签及其内容都能找到

  1. 正则表达式搜索,使用search方法来匹配内容

t_list = bs.find_all(re.compile(“a”)) 结果:含有a的标签及其内容都能找到

  1. kwargs 参数:通过属性进行定位

t_list = bs.find_all(id = “head”) 含有id=head这一属性的标签及其子内容

t_list = bs.find_all(class_=True) 有class属性的标签及其子内容

  1. text参数:通过文本中想要的内容定位

t_list = bs.find_all(text =“hao123”) 返回hao123字符串文本

t_list = bs.find_all(text = re.compile(“/d”)) 利用正则表达式查找包含特定文本的内容

  1. limit参数:限制想要的数量

t_list = bs.find_all(“a”,limit=3) 找出所有a,从前往后只找3个

2.4.2select()

  1. 以标签来查找

    t_list = bs.select(“a”) == t_list = bs.find_all(“a”)

  2. 以类名(manv)来查找

    t_list = bs.select(“.manv”)

  3. 以id(u1)来查找

    t_list = bs.select(“#u1”)

  4. 以属性来查找

    t_list = bs.select(“a[class=’manv’]”)

  5. 通过父子标签查找

    t_list = bs.select(“head>tittle”)

  6. 通过类名(mnav)找到类名为(bri)的兄弟标签

    t_list = bs.select(“.mnav~.bri”)

  7. 获得文本内容

    t_llist.get_text()

3.selenium

3.1基础用法

  1. 创建浏览器对象
1
2
from selenium import webdriver
driver = webdriver.Firefox()

请求页面

1
driver.get("https://www.baidu.com/")

!!!无头请求模式:以Firefox为例,谷歌就把Firefox改成Chrome

1
2
3
4
5

options = webdriver.FirefoxOptions()
options.add_argument('-headless')
driver = webdriver.Firefox(firefox_options=options)
driver.get('http://www.baidu.com/')

页面的基本操作(点击、输入)

1
2
3

driver.find_element_by_id('kw').send_keys('想要检索的内容') #输入
driver.find_element_by_id('su').click() #点击

保存网页截图

1
2

driver.save_screenshot('baidu.png')

获取渲染之后的数据

1
2
3

print(driver.page_source)
#并不是获取网页源码,而是element下面的代码,对于动态网页来说,源码是看不到的

据说上面方法获取的页面源码不标准,下面介绍另一种方法:

1
2

html = driver.find_element_by_xpath("//*").get_attribute("outerHTML")

请求页面后的cookie值

1
2

print(driver.get_cookies())

查看当前页面路径

1
2

print(driver.current_url)

关闭页面和浏览器

1
2


  1. ```

    关闭页面

    driver.close()

    关闭浏览器

    driver.quie()

    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

    ## 3.2 元素定位的方法

    1. 通过标签的id定位

    `ret1 = driver.find_element_by_id("anony-nav")`

    以对象形式保存

    2. 通过标签的id值获取多个标签

    `ret2 = driver.find_elements_by_id("anony-nav")`

    以列表形式保存,元素为对象

    3. 通过标签的class属性值获取标签

    `ret3 = driver.find_elements_by_class_name(" ")`

    同样是列表形式,这个属性有多少标签就对应多少个对象

    4. 通过xpath获取a标签

    `ret4 = driver.find_elements_by_xpath("")`

    括号中的内容通过在浏览器中右击a标签–copy–coyp xpath,获取其在标签树上的路径

    5. 通过标签包裹的文本“下载豆瓣App”获取元素列表

    `ret5 = driver.find_element_by_link_text("下载豆瓣App")`

    6. 通过包含的文本内容模糊定位

    `ret6 = driver.find_elements_by_partial_link_text(“豆瓣)`

    7. 通过标签名获取元素列表

    `ret7 = driver.find_elements_by_tag_name("div")`

    8. 获取`<h1>`标签包裹的文本内容及属性
    1
    2

    1.

    1.先找到元素对象

    ret8 = driver.find_element_by_tag_name(“h1”)

    2.获取元素内容

    ret8.text

    3.获取href属性值

    ret8.get_attribute(“href”)

    1
    2
    3
    4
    5
    6

    ## 3.3 selenium的其他方法

    ### switch页面

    1. 切换浏览器窗口

    1.获取当前所有窗口

    current_windows = driver.window_handles
    以列表的形式保存

    2.根据窗口索引进行切换

    driver.switch_to.window(current_windows[1])

    1
    2

    控制页面的前进和后退

    driver.forward()
    driver.back()

    1
    2
    3
    4
    5
    6
    7
    8

    switch_to进入嵌套网页

    iframe是html中常用的一种技术,即一个页面中嵌套了另一个网页,selenium默认访问不了iframe中的内容,比如登录窗口(通过控制台可以看到登录窗口实际是另一个网站),对应的解决思路是:

    `driver.switch_to.frame(0)`括号内是索引值,从0开始

    ### 进行自动化登录

先点击密码登录

driver.find_element_by_xpath(“”).click()

获取输入框标签

driver.find_element_by_xpath(“input的路径”).send_keys(“账号/密码”)

最后点击登录

driver.find_element_by_xpath(“”).click(q)

1
2

触发某个事件之后,页面出现弹框提示,处理这个提示或者获取提示信息方法如下:

alert = driver.switch_to_alert()

接收警告框内容

text = driver.switch_to_alert().text
print(text)

处理警告框

1.点击确认

driver.switch_to.alert.accept()

2.点击取消

driver.switch_to.alert.dismiss()

3.如果alert弹框上有文本框,可以输入文字

driver.switch_to.alert.sendkeys()
```

4.