【YushenFeb】Python爬虫基础

311 阅读12分钟

一、初识Python爬虫

1.1 什么是爬虫?

  • 爬虫的概念:通过编写程序,模拟浏览器上网,然后让其去互联网上抓取数据的过程。

1.2 爬虫有什么风险?

  • 爬虫具有的风险主要体现在以下两个方面:
  1. 爬虫干扰了被访问网站的正常运营
  2. 爬虫抓取了受到法律保护的特定类型的数据或信息

1.3 如何规避爬虫风险?

  • 避免爬虫风险的两种方法:
  1. 时常优化自己的程序,避免干扰被访问网站的正常运行。
  2. 在使用,传播爬取到的数据时,审查抓取到的内容,如果发现涉及到用户隐私及商业机密等敏感内容需要及时停止爬取或传播。

1.4 爬虫在使用场景中的分类

类别作用
通用爬虫抓取系统重要组成部分,抓取的是一整张页面数据。
聚焦爬虫建立在通用爬虫的基础之上,抓取的是页面中特定的局部内容。
增量式爬虫检测网站中数据更新的情况,只会抓取网站中最新更新出来的数据。

1.5 什么是反爬机制与反反爬策略?

  • 反爬机制的概念:门户网站,可以通过制定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬取。
  • 反反爬策略的概念:爬虫程序可以通过制定相关的策略或者技术手段,破解门户网站中具备的反爬机制,从而可以获取门户网站中相关的数据。

1.5.1 最早的反爬协议

  • robots.txt协议:是一种君子协议,规定了网站中那些数据是可以被爬虫程序爬取,那些数据不可以被爬取。

1.6 超文本传输协议

1.6.1 Http协议

  • Http协议概念:就是服务器和客户端进行数据交互的一种形式。
常用请求头信息简介
User-Agent请求载体的身份标识。首部包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号。
Connection请求完毕后,是断开连接还是保持连接。决定当前的事务完成后,是否会关闭网络连接。如果该值是“keep-alive”,网络连接就是持久的,不会关闭,使得对同一个服务器的请求可以继续在该连接上完成。
常用响应头信息简介
Content-Type服务器响应回客户端的数据类型,用于定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件。

1.6.2 Https协议

  • 概念:安全的超文本传输协议,涉及到数据加密。
加密方式简介
对称密钥加密对称密钥加密又叫专用密钥加密或共享密钥加密,即发送和接收数据的双方必使用相同的密钥对明文进行加密和解密运算。
非对称密钥加密公开密钥加密(Public-key Cryptography)也称为非对称密钥加密(Asymmetric Cryptography),是一种密码学算法类型。该加密算法使用两个不同的密钥:加密密钥和解密密钥。
证书密钥加密由服务器和客户端均认可的证书机构,对公钥做数字签名。然后分配已签名的公开密钥,并将密钥放在证书中进行绑定。服务器将数字证书发送给客户端,客户端可通过数字签名来验明公钥真伪。一旦确定信息无误后,客户端就会通过公钥对报文进行加密发送,服务器收到后再通过私钥解密。

二、Requests模块

2.1 什么是Requests模块?

  • Requests模块的概念:Python中原生的一款基于网络请求的模块,功能非常强大,简单便捷,效率极高。
  • Requests模块的作用:模拟浏览器发请求。

2.2 Requests模块的编码流程

graph TD
Step1:指定URL --> Step2:发起请求
Step2:发起请求 --> Step3:获取响应数据
Step3:获取响应数据 --> Step4:持久化存储

2.3 Requests模块安装

方法一:通过PyCharm图形化界面安装

  • Step1:PyCharm快捷工具栏File中单击Settings(Ctrl+Alt+S): requests_1.png
  • Step2:在所需Requests模块的Project中选择Python Interpreter界面里的+号: requests_2.png
  • Step3:搜索Requests模块并单击Install Package进行安装: requersts_3.png
  • Step4:显示Package 'requests' installed successfully则安装成功 requersts_4.png

方法二:通过命令行进行安装

pip install requests

2.4 UA检测与UA伪装

名称介绍
UAUser-Agent(请求载体的身份标识)
UA检测80%以上的门户网站均采用了UA检测反爬机制。门户网站的服务器会检测对应请求的载体身份标识,如果检测到请求的载体身份标识不是基于某一款浏览器的,则表示该请求为不正常请求(爬虫),则服务器端很有可能拒绝该次请求。
UA伪装让爬虫对应的请求载体身份标识伪装成某一款浏览器。

2.5 项目实战

★需求:爬取搜狗首页的页面数据

import io
import requests

if __name__ == "__main__":
    url = 'https://www.sogou.com/'    #Step1:指定URL
    response = requests.get(url=url)    #Step2:发起请求,get方法会返回一个响应对象
    page_text = response.text    #Step3:获取响应数据,text返回的是字符串形式的响应数据
    print(page_text)
    with io.open('./sogou.html','w',encoding='utf-8') as fp:    #Step4:持久化存储
        fp.write(page_text)
    print('爬取数据结束!')

★需求:爬取搜狗指定词条对应的搜索结果页面(简易网页采集器)

import requests

if __name__ == "__main__":
    url = 'https://www.sogou.com/web'    #指定URL
    #UA伪装:将对应的User-Agent封装到一个字典中(通过浏览器自带抓包工具或抓包软件获取UA)
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36'
    }
    #处理URL携带的参数:封装到字典中
    kw = input('Enter a word:')
    param = {
        'query':'kw'
    }
    #对指定的URL发起请求:对应的URL是携带参数的,并且请求过程中处理了参数
    response = requests.get(url=url,params=param)
    page_text = response.text
    fileName = kw + '.html'
    with open(fileName,'w',encoding='utf-8') as fp:    #持久化存储
        fp.write(page_text)
    print(fileName,'保存成功!')

★需求:破解Baidu翻译

  • 项目分析:
  1. 需要获得页面中的局部数据,页面局部刷新。
  2. 分析数据包,发现为post请求(携带了参数)。
  3. 响应是一组json数据。
import requests
import json

if __name__ == "__main__":
    post_url = 'https://fanyi.baidu.com/sug'    #指定URL
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36'
    }    #进行UA伪装
    word = input('Enter a word:')    #Post请求参数处理(同Get请求一致)
    data = {
        'kw':word
    }
    response = requests.post(url=post_url,data=data,headers=headers)    #发送请求
    #获取响应数据:json()方法返回的就是obj(如果确认响应数据是json类型的,才可以使用json)
    dic_obj = response.json()
    print(dic_obj)
    fileName = word + '.json'    #持久化存储
    fp = open(fileName,'w',encoding='utf-8')
    json.dump(dic_obj,fp=fp,ensure_ascii=False)
    print('Over!')

★需求:爬取豆瓣电影分类排行榜中科幻电影排行详情数据

import requests
import json

if __name__ == "__main__":
    #指定URL及请求参数(分析指定页面数据包获得)
    url = 'https://movie.douban.com/j/chart/top_list'
    param = {
        'type':'17',
        'interval_id':'100:90',
        'action':'',
        'start':'0',    #从库中第几部电影开始取
        'limit':'20',    #一次取出的电影个数
    }
    #进行UA伪装
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36'
    }
    #发送requests请求
    response = requests.get(url=url,params=param,headers=headers)
    #获取响应数据
    list_data = response.json()
    #持久化存储
    fp = open('./douban.json','w',encoding='utf-8')
    json.dump(list_data,fp=fp,ensure_ascii=False)
    print('Over!')

★需求:爬取肯德基餐厅查询中指定地点的餐厅数

import requests

if __name__ == "__main__":
    #指定URL及请求参数
    post_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx'
    word = input('Enter a city:')
    data = {
        'op':'keyword',
        'cname':'',
        'pid':'',
        'keyword':word,
        'pageIndex':'1',    #显示第几页
        'pageSize':'10',    #每页显示的最大条目数
    }
    #进行UA伪装
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36'
    }
    #发送requests请求
    response = requests.post(url=post_url,data=data,headers=headers)
    #获取响应数据
    list_text = response.text
    fileName = word + '.txt'
    #持久化存储
    with open(fileName,'w',encoding='utf-8') as fp:
        fp.write(list_text)
    print(fileName,'保存成功!')

★Tips:

  • Payload中所有参数均要在data字典中包含,否则就会出现-1000的结果。

三、数据解析

3.1 什么是数据解析?

  • 数据解析的概念:对网页源码进行分析,获取指定内容的过程。
  • 数据解析的应用:常用于(75%以上的需求)聚焦爬虫。

3.2 数据解析的编码流程

graph TD
Step1:指定URL --> Step2:发起请求
Step2:发起请求 --> Step3:获取响应数据
Step3:获取响应数据 --> Step4:数据解析
Step4:数据解析 --> Step5:持久化存储

3.3 数据解析原理

★需要解析的局部文本内容都会在标签之间或者标签对应的属性中进行存储,通过数据解析方法对指定标签进行定位,然后对标签或者标签对应的属性中存储的数据进行提取(解析)。

3.4 数据解析方法

3.4.1 正则匹配

  • 正则匹配的概念:是一个特殊的字符序列,它能帮助用户便捷地检查一个字符串是否与某种模式匹配。
  • 环境安装:Python的正则模块是re,是Python的内置模块,不需要安装,导入即可。

★正则匹配基本语法

  • 单字符匹配:
单字符匹配匹配对象
.匹配任意字符(不包括\n)
[][aoe][a-w]集合中任意一个字符
\d数字[0-9]
\D非数字[0-9]
\w数字、字母、下划线、中文
\W非数字、字母、下划线、中文
\s所有空白字符包括空格、制表符、换页符等。等价于:[\f\n\r\t\v]
\S非空白字符
[\u4e00-\u9fa5]中文
  • 匹配数量限制:
匹配数量限制数量含义
*任意多次:>=0
+至少1次:>=1
?可有可无,即0次或者1次
{m}前一个字符固定m次
{m,}前一个字符至少m次
{m,n}前一个字符m~n次
{m,n}?前一个字符m~n次,并取尽可能少的情况
  • 匹配边界限制:
匹配边界限制边界含义
$以XXX结尾
^以XXX开头
\A只在字符串开头进行匹配
\Z只在字符串结尾进行匹配
\b匹配位于开头或者结尾的空字符串
\B匹配不位于开头或者结尾的空字符串
  • 匹配分组:
匹配分组分组含义
(AB)将AB划为一个元组
  • 匹配模式:
匹配模式模式含义
.*贪婪模式
.*?非贪婪(惰性)模式
  • 匹配大小写
匹配大小写匹配含义
re.I忽略大小写
  • 匹配单/多行
匹配单/多行匹配含义
re.M多行匹配
re.S单行匹配
  • 正则处理函数
匹配替换匹配含义
re.match(pattern,string,flags=0)尝试从字符串的开始位置匹配一个模式,如果匹配成功,就返回一个匹配成功的对象,否则返回None。
re.search(pattern,string,flags=0)扫描整个字符串并返回第一次成功匹配的对象,如果匹配失败,就返回None
re.findall(pattern,string,flags=0)获取字符串中所有匹配的字符串,并以列表的形式返回
re.sub(pattern, repl,string,count=0,flags=0)用于替换字符串中的匹配项,如果没有匹配的项则返回没有匹配的字符串
re.compile(pattern[ ,flags])用于编译正则表达式,生成一个正则表达式(Pattern)对象,供match()和search()函数使用

3.4.2 BeautifulSoup4(BS4)

  • BS4的概念:全称为BeautifulSoup4,可以在HTML或XML文件中提取数据的网页信息提取库。
  • 环境安装:先安装lxml库再安装bs4库。
pip install lxml
pip install bs4
  • BS4数据解析的原理:①实例化一个BeautifulSoup对象,并将页面源码数据加载到该对象中。②通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取。

★BS4实例化

  • 将本地html文档中的数据加载到该对象中:
fp = open('./test.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
  • 将互联网上获取的页面源码数据加载到该对象中:
page_text = response.text
soup = BeautifulSoup(page_text,'lxml')
  • BS4实例化示例:
from bs4 import BeautifulSoup

html_test = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

soup = BeautifulSoup(html_doc, features='lxml')    #soup实例化html_test
print(soup.prettify())    #打印soup对象

★Tips:

  • 因为BeautifulSoup是一个类,所以要实例化类,参数html_test是要转化为soup对象的数据,features则是要转化为soup对象时编码的方法:soup = BeautifulSoup(html_doc, features='lxml')
  • features='lxml':解析器,就是把lxml数据转化为soup数据的编码方法。

★BS4基本语法

①soup.tagName:
  • 返回的是文档中第一次出现的tagName对应的标签。
②soup.find():
  • find('tagName'):等同于soup.div
  • 属性定位:soup.find('div',class_/id/attr='song')
③soup.find_all('tagName'):
  • 返回符合要求的所有标签(列表)
④soup.select():
  • select('某种选择器(id、class、tag...)'),返回的是一个列表。
  • id使用格式:#id
  • 标签使用格式:tag
  • 类使用格式:class
⑤层级选择器:
#方法一:逐个标签选择
soup.select('.tang > ul > li > a')[0]    #层级选择器的返回值是一个列表,首个元素序号为0,“>”表示一个层级。
#方法二:间隔标签选择
soup.select('.tang > ul a')[0]    #将“>”替换为空格则表示两者标签之间跨过多个标签。
⑥获取标签之间的文本数据:
soup.a.text/get_text()    #text/get_text()可以获取某一个标签中所有的文本内容。
soup.a.string    #string只可以获取该标签下直系的文本内容。

3.4.3 Xpath

3.4.4 三种数据解析方法对比

1:re模块:语法简单,但是不好编写而且阅读性差 2:xpath模块:使用方法简单,但是要记住xpath语法,并且在使用的时候还要把数据转化为element对象才可以使用xpath语法进行索引导航 3:bs4:可以说是re模块和xpath模块的混合版,可以使用特定的字符串来索引,并且不需要把数据进行转化,但是要对数据进行编译为soup对象,才可以使用soup对象中的方法 总结:这三种解析数据的方法都各有利弊,我们在解析网页数据的时候,只要寻找最合适的解析数据的方法就可以了,这三个解析方法没有没有高低之分,只要找到最合适的解析方法就合适了。

与re和xpath模块的区别: re模块:使用起来过于麻烦且阅读性不好 xpath模块:需要使用一些特定的语法 bs4模块:只需要记住一些方法如:find()、find_all(),后面会发现bs4可以认为是re和xpath的混合使用 总结 在爬取不同的网站的时候,寻找最适合的解析数据方法 可以与别的解析方法进行比较

3.5 项目实战

3.5.1 正则匹配项目实战

3.5.1 BS4匹配项目实战

3.5.1 Xpath匹配项目实战