欢迎光临散文网 会员登陆 & 注册

2020年Python爬虫全套课程(学完可做项目)

2022-07-16 19:20 作者:无双小骏  | 我要投稿

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

爬虫的价值体现在实际应用和就业两方面。

爬虫在法律中是不被禁止的,但是具有违法的风险,好比一把锋利的水果刀,可以用来削苹果,同样也可以进行暴力。所以爬虫可以分为善意爬虫和恶意爬虫。

爬虫所带来的风险可以体现在如下两方面:

  1. 爬虫干扰了被访问网站的正常运营
  2. 爬虫抓去了受到法律保护的特定类型的数据或信息

如何在使用或编写爬虫的构成中避免进入局子的厄运呢?

  1. 时常优化自己的程序,避免干扰被访问网站的正常运行
  2. 在使用或传播爬取到的数据时,需要审查抓取到的内容,若发现了涉及到用户隐私或商业机密等敏感内容就需要及时停止

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

通用爬虫:爬取系统重要组成的部分,抓取的是互联网当中的一整张页面数据。

聚焦爬虫:建立在通用爬虫的基础之上,抓取的是页面中特定的局部内容。

增量式爬虫:检测网站中数据更新的情况,只会抓取网站中最新更新出来的数据。

反爬机制:门户网站,可以通过制定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬取。

反反爬策略:爬虫程序可以通过制定相关的策略或者技术手段,破解门户网站中具备的反爬机制,从而可以获取门户网站信息。

robots.txt协议:君子协议。规定了网站中哪些数据可以被爬取,哪些数据不可以被爬取。可在任何网站的后面加上robots.txt来查看,allow是可以爬取,disallow是不可爬取。

http协议:服务器和客户端之间进行数据交互的一种形式。例如说人与人之间进行沟通用的是语言,亦或者在智取威虎山当中的黑话。

常用的请求头信息:

  1. user-agent:请求载体的身份标识,会显示出我所们用的浏览器和电脑的版本位数等等的信息(除了可以使用浏览器来爬取之外也可以使用代码来伪装一个身份标识)
  2. Connection:请求完毕后,是断开连接还是保持连接。

常用响应头信息:Content-Type:服务器响应回客户端的数据类型

https协议:安全的超文本传输协议,相比于http而言更加地安全,是进行数据加密的。

加密方式:

  1. 对称秘钥加密:相当于在客户端向服务器发送信息的时候,对信息进行加锁,同时会产生一个密钥,然后发送过程中将密钥和加密的文件同时传过服务器。这样有一个缺点就是在进行密钥和密文传输的过程中有可能会被第三方拦截工具拦截从而暴露。
  2. 非对称密钥加密:可以解决对称秘钥加密的安全隐患。首先让服务器端设定一个加密方式发送给客户端,客户端在拿到了该加密方式之后,针对该加密方式设定出一个密文发给服务器(公钥),由于密钥的制定者是服务器,因此服务器在拿到密文之后就可以根据自己所指定的密钥进行解密(私钥)。这样做的缺点有两个,一个是效率比较低,另一点是无法保证在服务器端在传输给客户端密钥的时候该密钥不会被拦截并篡改,并在篡改之后将密钥发给客户端。
  3. 证书秘钥加密(https):在针对非对称密钥加密的缺点上进行改良,同时在服务器端与客户端在足够信任的基础上诞生了证书密钥机构,理解为将公钥先发给我证书认购机构,认购机构对其进行签名后将证书发送给客户端,客户端只有看到签名后才会认为这是从服务器端所发来的。如下图:

requests模块:python中原生的一款基于网络请求的模块。功能强大且使用起来简单便捷效率极高。作用:模拟浏览器发发请求。

在涉及到网络请求的模块有两个,一个是urllib,另一个是requests。urllib模块比较古老,相对于requests更为复杂与麻烦,因此后续所学习的内容均以requests为主。

使用方式:(requests模块的编码流程)

  1. 指定url(以字符串的形式指定)
  2. 发起请求
  3. 获取响应数据
  4. 持久化存储(存储的是响应数据)

在开始前我们要配置环境,电脑里要有requests:win+r然后cmd,在里面输入pip install requests即可。

实战——爬取搜狗首页

import requests
if __name__ == "__main__":
    url = 'https://www.sougou.com/'
    response = requests.get(url=url)
    page_text = response.text
    print(page_text)
    with open('./sougou.html','w',encoding='utf-8') as fp:
        fp.write(page_text)
    print('爬取数据结束!!!')

实战——网页采集器

#User-Agent(请求载体的身份标识)
#UA伪装:门户网站的服务器会检测对应请求的载体身份标识,如果检测到请求和载体身份标识为某一款浏览器则会通过
#说明和这个请求是一个正常的请求,但如果检测到的请求载体身份标识不是基于某一款浏览器的,则表示该请求为不正常请求(爬虫)
#服务器端就很有可能拒绝该次请求
#UA伪装:让爬虫对应的请求载体身份标称伪装成某一款浏览器
if __name__ == "__main__":
    #UA伪装:将对应的User-Agent封装到一个字典中
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44"}
    url = 'https://www.sogou.com/web'    #后面之所以是乱码,这是由于url编码的特性决定的,但是在我们的requests模块中也是可以用中文的。
    #因为我们要设置的是动态的搜索框,而不是只搜索特定的内容,因此我们要处理url所携带的参数,设置一个字典。
    kw = input('enter a word')
    param = {
        'query':kw
    }
    #对指定的url发起的请求对应的url是携带参数的,并且请求过程中处理了参数
    response = requests.get(url=url,params=param,headers=headers)       #parmas是设置参数,我们在之前把parma设置为一个字典,自字典的内容使我们所输入的内容。

    page_text = response.text
    fileName = kw+'html'
    with open(fileName,'w',encoding='utf-8') as fp:
        fp.write(page_text)
    print(fileName,'保存成功!!!')

实战——破解百度翻译

POST请求通常是携带参数的,我们要用requests模块发送一个post请求,而在Response-Headers下的Content-Type所表示的是服务器端响应客户端请求的类型,这个类型是一个json串。这个json串就在Response下。

if __name__ == "__main__":
    #指定url
    post_url = 'https://fanyi.baidu.com/sug/'
    #进行UA伪装
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44"}
    #post请求参数处理(同get请求一致)
    word = input('enter a word!')
    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!!!')

实战——爬取豆瓣网站

if __name__ == "__main__":
    url = 'https://movie.douban.com/j/search_subjects?'
    para = {
        'type': 'movie',
        'tag': '经典',
        'sort': 'recommend',
        'page_limit': '20',
        'page_start': '40'
    }
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44"}
    response = requests.get(url=url,params=para,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!')

实战——爬取肯德基地址

if __name__ == "__main__":
    url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
    para = {
        'cname':'',
        'pid':'',
        'keyword': '北京',
        'pageIndex': '1',
        'pageSize': '10'
    }
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44"}
    response = requests.get(url=url,params=para,headers=headers)
    list_data = response.text
    fp = open('./kfc','w',encoding='utf-8')
    json.dump(list_data,fp=fp,ensure_ascii=True)
    print('over!')

实战——爬取药监总局

这个网站的内容中,我们要爬取的内容是动态加载出来的数据,大概率是阿贾克斯所制作的数据,因此我们无法通过传统的方式来获取(无法直接对url发送请求即可或许),而接下来我就就来进行验证是否真的是阿贾克斯请求导致的动态网址。

我们刷新该网址,然后可以在开发者人员模式下的网络里找到XHR,里面有一行,点开之后查看Content-TYPE后能发现是json格式的,然后点击response后可以看到这个json串,在百度里搜索json在线就能出现一个解析格式化的网址,将这个json串剪切到里面就可以查看到我们首页当中所显示的行政机关、日期和许可编号之类的。意味着当前页面中的信息是我们的阿贾克斯请求所请求到的。

通过对这个json串进行深度剖析之后发现并没有现成的url,但是却有id,通过对每个网页当中不同的链接进行观察会发现,每一个链接的唯一区别就只有id不一样,所以我们可以根据每家企业所对应的id值能够和网页的域名拼接出完整的url地址。

而我们在首页已经得到了一个教训就是网页当中的内容可能是动态加载的,因此我们不可能轻松地搞定,为了防止每一个链接下也是如此,所以我们要进行验证,打开任意一个链接,然后在这个链接下在开发者人员工具模式下可以看到content-TYPE中显示该网页是html格式,然后再response中搜索网页中的文字但是搜不到,因此我们可以判定这个网页也是动态加载数据。然后我们在XHR中可以看到又出现了一个阿贾克斯的Name,然后将该Name的response下的json串继续在线解析格式化输出看一下,就可以发现这些内容里就拥有我们想要爬取的最终的内容。最后通过对比不同的链接可以发现,每一个链接的post请求的url都是相同的,只有参数id不同。因此倘若我们可以批量夺取多家企业的id后,就可以将id和url形成一个万能蒸的详情页对应想求数据的阿贾克斯请求的url。

那么进入代码环节,因为我们要爬取的内容在药品官网的链接里的阿贾克斯请求POST里面,所以我们的url是在网络的XHR里的那个url里。同时json串都是有封装的,其封装内容是在最下面的from data里,我们在代码里创建一个data的列表将其进行封装即可。在搞好了url、headesUA反爬、data封装之后,将这个json串进行保存赋值给json_ids,下一步就可以将json_ids里面的id进行提取并存储了。存储的话我们要存储在列表里,因此要提前建立一个列表这里命名为id_list,然后通过循环将id安置在列表当中,同时还要知道我们的id是在哪里,观察json在线解析里可得知每一个id都在医药首页的XHR阿贾克斯中json串所显示的的response的list中的列表里,如下图所示:通过查看可得知id在list的列表中是键,而id的内容在list列表中是值。

我们可以用尾部追加append的方式一个个地将list中的id一个个地打印到我们所创建的空列表当中,批量获取id完成,接下来就要获取企业详情页的数据。

之前我们分析过每一个链接都是一样的,不同时只是id,因此我们可以通过循环,for id in id_list,意思就是设id是我们刚才所创建的存储id的列表的id_list的表达形式,然后封装的时候就以data的列表命名,然后键是id,值就是id_list里面的内容,最后通过requests的post请求,设置好网址url,还有反爬UA,最后还有post所对应的data就是刚封装好的id,以json的形式组合完成命名为derail_json,最后再将其通过追加append的方式保存到all_data_list当中,保存为json钱再将jsonimport一下。

我们下一步就要实行分页操作,因此要做一下循环,负责搞定网页的爬取的分页的在data里的page,range(1,6),根据顾头不顾尾的方式是循环五次(1,2,3,4,5)的意思,因此将page的类型先确定为是字符串而并非是数字,然后在data字典里的page就变成了(1,2,3,4,5),获取前五页所有企业的id进而获得所有企业的详情数据。

总结:首先讲解的是什么是requests模块,requests模块就是一个模拟浏览器发送请求的工具。使用方式大致分为:指定url(包括UA尾伪装和请求参数的处理)、发送请求、获取相应数据、持久化存储四个步骤。接下来就要摄入到数据解析的讲解过程当中,主要应用在聚焦爬虫(一整张页面的当中局部的内容进行爬取)。

数据解析分类:正则、bs4、xpath(常用)

大部分网页要存储的位置,无非只有两种,一种是存放在标签中间,另一种是存放在标签所对应的属性中,我们最终想要拿到这些数据,只需要找到标签的位置或存储标签所对应的属性值存储下来。

正则解析原理:

  1. 进行指定标签的定位
  2. 标签或者标签定位的属性中存储的数据值进行提取(解析)

正则练习

图片数据的爬取,最后用text对图片的url进行返回的结果是字符串形式的数据,而如果使用content返回的是二进制数据,json返回的是对象。想要获取图片我们用的是content,然后保存的话就是with open来保存。

import requests
url='https://static.qiushibaike.com/images/download/slogan.png'
img_data = requests.get(url=url).content
with open('./picture.jpg','wb') as fp:
    fp.write(img_data)

实战——爬取减肥药品信息(不保存)

import requests, re,csv
url = 'https://www.111.com.cn/search/search.action?keyWord=%25E5%2587%258F%25E8%2582%25A5%25E8%258D%25AF'
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"}
response = requests.get(url, headers=headers)
pattern = re.compile('<li id="producteg_.*?".*?'
                     'alt="(?P<name>.*?)".*?'
                     '<span>(?P<price>.*?)</span>.*?'
                     ' href="//(?P<href>.*?)".*?'
                     '评论 <em>(?P<comment>.*?)</em>条',re.S)
Drugsli_list = pattern.finditer(response.text)
for drug in Drugsli_list:
    dic = drug.groupdict()
    print(dic)
    dic['price'] = drug.group('price').strip()+"元"
    dic['href'] = 'https://' + drug.group('href')
    dic['comment'] = drug.group('comment')+"条评论"
print("结束!")

bs4是python语言中独有的,不像正则可以在python,也可以在其它的语言中运用,在运用的时候需要的环境除了bs4在之还需要lxml。

bs4数据解析原理:

  1. 实例化一个BeautifulSoup对象,并且将页面源代码数据加载到该对象中。
  2. 通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取

如何实例化BeautifulSoup对象:

from bs4 import BeautifulSoup

one:将本地的html文档中的数据加载到该对象中

fp = open('./test.html','r',encoding='utf-8')

soup = BeautifulSoup(fp,'lxml')

two:将互联网上获取的页面源码加载到该对象中

page_text = response.text

soup = BeautifulSoup(page_text,'lxml')

提供的用于数据解析的方法和属性:

  1. soup.find('tagName') = soup.tagName:返回的是文档中第一次出现的tagName标签(这里的tagName是一个参数,意为标签的名字,例如soup.a意思是返回第一个出现的a标签的内容)
  2. 根据属性去针对标签进行选择 。soup.find('div',class_='song')。意思是找到属性为song的div标签。class要加下划线是因为如果不加下划线的话就不是一个参数名称,而是一个关键字了。
  3. find_all:找到符合条件的所有标签。soup.find_all('a'),返回的就是所有的a标签,相比于find,不再只输出第一个了。
  4. select:参数当中可以放置一些选择器,返回的是一个列表。select('某种选择器(id,class,标签...)')。层级选择器:也可以通过索引的形式进行返回,例如select('.tang > ul > li > a')[0],同时这里面的>是表示也层级,空格时表示多个层级。

我们可以看到最后输出的内容弄个大多都套在标签当中。

获取标签之间的文本数据

  1. soup.a.text/string/get_text()
  2. text/get_text():可以获取某一个标签中所有的文本内容
  3. string:只可以获取该标签下面直系的文本内容

获取标签中属性的值

  1. 直接写属性名称。eg:soup.a['href']

bs4实战训练——爬取三国演义所有章节的标题与内容

设计思路:先使用通用爬虫,将三国演义的页面中的所有文本标题进行获取,再将每一个标题所对应的详情页进行获取。每一个标题可以点击意味着这里每一个标题都对应着链接的地址,对这个地址发起求就可以获取到详情页。

首先对首页的页面数据进行爬取(指定url),再使用requests发送请求并设置UA反爬。

import requests
from bs4 import BeautifulSoup
import time
url = "https://www.shicimingju.com/book/sanguoyanyi.html"
headers = {"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"}
response = requests.get(url, headers = headers)

接下来在首页中解析出章节的标题和详情页的url,首先实例化BeautifulSoup对象,需要将页面源代码加载到该对象当中。

soup=BeautifulSoup(page_text.content,'lxml')

在获取标题的时候我们要保证获取的是复数,而find获取的是单数,因此可以选择的是select和find_all,这里用的是select层级选择器。

li_list = soup.select('.book-mulu > ul >li')

通过开发者人员工具可以看到网址中的标题名在book-mulu属性下的ul下的li,这样就能将a标签所处的li标签全部选取,然后通过循环,以find的方式将a标签总的标题和链接取出来,此时要注意的是取出来的href值不是完整的,需要进行字符串的拼接。

fp = open('./sanguoyanyi.txt','w',encoding='utf-8')
for li in li_list:
    title = li.a.string
    detail_url = 'http://www.shicimingju.com'+li.a['href']
    detail_page_text = requests.get(url=detail_url, headers=headers).text

而下一步我们要做的就是对详情页发起请求,解析出章节内容。并且解析出详情页相关的章节内容。我们之前实例化的soup只能解析出首页的内容,不能实例化详情页的内容,因此我们要再实例化一个。每一段的详情页内容都在div属性为chapter_content里,直接用find即可,此刻我们就解析到了章节的内容。

detail_soup = BeautifulSoup(detail_page_text,'lxml')
div_tag = detail_soup.find('div',class_='chapter_content').text
content = div_tag
fp.write(title+':'+content+'\n')
print(title,'爬取成功!!!')

最后就可以做持久化存储了,注意with open不能像写到循环里,这个文本文件只需要被打开一次次,因此要先创建一个文本文档命名任意,编码格式为utf-8的前提下,写入标题和内容,标题后面对应的是冒号,冒号后对应的是内容,最后面再加上一个\n的分隔符。结束!

Xpath解析:最常用且最便捷高效的一种解析方式。

Xpath解析原理:

  1. 实例化一个etree的对象,且需要将被解析的页面源码数据加载到该对象中。
  2. 调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获。

lxml是一种解析器,在xpath爬取前安装。

如何实例化一个etree对象:

  1. 将本地的html文档中的源码数据加载到etree对象中。etree.parse(filePath)
  2. 可以将从互联网上获取的掩码数据加载到对象当中。etree.HTML('page_text')

xpath是根据层级关系去做的标签定位。从根节点(根目录:最外层标签的节点)逐步递归找到我们所要查找的内容。最终所返回的将是一个列表,列表当中存储的是我们所查找的节点,返回的并不是文档内容,而是一个Element对象当中进行的一个存储。而如果当我们所查找的标签为多个时,所返回的内容也是多个Element类型的对象,想要获取文本就在后面加上/text()

xpath表达式:

  • /:表示的是从根节点开始定位,表示的是一个层级。
  • //:表示的是多个层级,表示从任意位置开始定位。
  • 属性定位://div[@class=’song‘] 格式为:tag[@attrName='value']
  • 索引定位://div[@class='song']/p[3] 。索引是从1开始的。
  • 取文本
  • /text() 获取的是标签中直系的文本内容
  • //text()获取的是非直系的文本内容(所有文本内容)
  • 取属性:/@attrName:直接/@加上属性名称(eg:img/@src)

xpath实战——58二手房

在获取完网页源码之后进行数据解析,随后为了获取标题,通过观察网络源码可以发现标题存在于li标签之下,因此我们先定位到li标签后进行循环,在此循环下再进行xpath表达式,在这个li循环中的xpath表达式下我们要注意,在最前面要用./来代表li所指向的局部的内容。

if __name__ == "__main__":
    # 爬取到页面源码数据
    url = 'https://bj.58.com/ershoufang/'
    headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"}
    page_text = requests.get(url=url,headers=headers).text
    # 解析数据
    tree = etree.HTML(page_text)
    # 存储的就是li标签对象
    li_list=tree.xpath('//ul[@class="house-list-wrap"]/li')
    fp = open('58.txt','w',encoding='utf-8')
    for li in li_list:
        title = li.xpath('./div[2]/h2/a/text()')[0]
        print(title)
        fp.write(title+'\n')

实战——使用xpath爬取城市名称

if __name__ == "__main__":
    # 爬取到页面源码数据
    url = 'https://www.aqistudy.cn/historydata/'
    headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"}
    page_text = requests.get(url=url,headers=headers).text
    # 解析数据
    tree = etree.HTML(page_text)
    host_li_list = tree.xpath('//div[@class="bottom"]/ul/li')
    all_city_names=[]
    for li in host_li_list:
        hot_city_name = li.xpath('./a/text()')[0]
        all_city_names.append(hot_city_name)
    city_names_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li')
    for li in city_names_list:
        city_names = li.xpath('./a/text()')[0]
        all_city_names.append(city_names)
    print(all_city_names,len(all_city_names))

同时xpath也可以先定位到我们要获得的文本的位置,然后再通过循环获取其位置下的文本,而倘若我们要获取的内容为多个,可以使用|运算符来解决。

if __name__ == "__main__":
    # 爬取到页面源码数据
    url = 'https://www.aqistudy.cn/historydata/'
    headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"}
    page_text = requests.get(url=url,headers=headers).text
    # 解析数据
    tree = etree.HTML(page_text)
    # '//div[@class="bottom"]/ul/li'
    # '//div[@class="bottom"]/ul/div[2]/li'
    a_list = tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a')
    all_city_names = []
    for a in a_list:
        city_name = a.xpath('./text()')[0]
        all_city_names.append(city_name)
    print(all_city_names,len(all_city_names))

实战——使用xpath爬取4k图片

我们要爬取的图片在a标签下的src的属性值。对图片进行请求后再对其保存,同时也要对alt的属性值进行提取,作为src的名字。

if __name__ == "__main__":
    # 爬取到页面源码数据
    url = 'http://pic.netbian.com/4kfengjing/'
    headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"}
    response = requests.get(url=url,headers=headers)
    #手动设定响应数据的编码格式
    # response.encoding = 'utf-8'
    page_text = response.text
    # 解析数据:src的属性值  alt属性
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//div[@class="slist"]/ul/li')
    # 创建一个文件夹
    if not os.path.exists('./picLibs'):
        os.mkdir('./picLibs')
    for li in li_list:
        img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
        img_name = li.xpath('./a/img/@alt')[0]+'.jpg'
        # 通用处理中文乱码的解决方案
        img_name = img_name.encode('iso-8859-1').decode('gbk')
        # print(img_name,img_src)
        # 请求图片进行持久化存储
        img_data = requests.get(url=url,headers=headers).content
        img_path = 'picLibs/'+img_name
        with open(img_path,'wb') as fp:
            fp.write(img_data)
            print(img_name,'下载成功!!!')

在发生乱码之后我们首先可以对其进行指定编码格式的操作,若无效课对其使用通用的处理中文乱码的解决方案。

验证码识别

反爬机制:验证码。

有一些网站在使用前需要登陆,登陆就离不开账号密码和验证码,此时的验证码就是一种门户网站的反爬机制。登陆操作我们可以使用requests模块进行模拟,但验证码需要我们取识别这个图片,用于模拟登陆操作。

识别验证码的操作

  • 人工肉眼识别(不推荐)
  • 第三方自动识别(推荐)
  • 云达码
  • 超级鹰
  • ......

代理

  • 某某网站会采取一些反爬措施,在单位时间内某一个ip请求过于频繁以至于超过了正常访问频率,就会进行ip的封禁。
  • 破解封IP这种反爬机制

什么是代理?

  • 代理服务器:网络信息中的中转站(将请求发送给web服务器,然后该服务器转发给我们要访问的网站)

代理的作用?

  • 突破自身IP访问的限制
  • 隐藏真实的IP受到攻击,隐藏自身真的IP

代理相关的网站:

  • 快代理
  • 西祠代理
  • www.goubanjia.com

代理IP的类型

  • http:应用到http协议对应的url中
  • https:应用到https协议对应的url中
# 代理报错的,试着将proxies中的"https"改成"https://",或者是将"http"改成"http://"
url = 'https://www.baidu.com/s?wd=ip'
headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"}
response = requests.get(url=url, headers=headers,proxies={"http":"47.101.44.122"}).text
with open('ip.html','w',encoding='utf-8') as fp:
    fp.write(response)

代理IP的匿名度:

  • 透明:服务器知道该次请求使用了代理,也知道请求对应的真是ip。
  • 匿名:知道使用了代理,不知道真实ip。
  • 高匿:不知道使用了代理,更不知道其真实的ip地址。

高性能异步爬虫

目的:在爬虫中使用异步实现爬取高性能的数据爬取操作。

urls = ['https://www.baidu.com/s?wd=ip','https://www.bilibili.com/';]
headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"}
def get_content(url):
    print('正在爬取:',url)
    response = requests.get(url=url, headers=headers)
    if response.status_code == 200:
        return response.content
def parse_content(content):
    print('响应数据的长度为:',len(content))
for url in urls:
    content = get_content(url)
    parse_content(content)

通过以上代码我们可以看出get方法是一个阻塞的方法,此时两个url就已经很慢了,那如果是20或者200个那就会更慢,这是因为我们的程序是单线程,只有拿到相应数据之后才会执行其它的方法,响应数据效率很慢。这时就需要使用异步操作去解决。

异步爬虫的两种方式:

  • 多线程,多进程:
  • 好处:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作就可以异步执行。
  • 弊端:无法无限制的开启多线程或者多进程(耗费cpu)。
  • 线程池、进程池:
  • 好处:我们可以降低系统对进程或者线程创建和销毁的一个频率,从而很好的降低系统的开销。
  • 弊端:池中线程或进程的数量是有上限的(有上限就会出现当阻塞操作超过线程数量时会变慢)。

之前我们在采集化妆品生产许可信息管理系统的时候会发现要爬取的内容为动态加载内容,我们就无法直接对url发请求去拿到的,验证其是否为动态加载数据的方式就是打开抓包工具,定位到network,刷新之后,打开对应大当前网页headers里url的response查看源码,搜索当前页面所显示的任意一家企业名称(搜索的快捷键为ctrl+f),发现并没有对应的企业名称,所以可以判断其为动态加载数据。

那么如果想知道当前页面下的动态加载数据是哪里来的,只需要在network下按ctrl+f进行全局搜索,与上面不同的是这次搜索的是全部的内容,就会找到我们要查询的动态加载数据的位置。把参数结合着url发布一个post请求就可以拿到企业名称(动态加载数据),方法很麻烦,因此就需要用到selenium来解决,同时也可以通过selenium实现模拟登陆。

What is selenuim?

  • 基于浏览器自动化的一个模块
  • 浏览器自动化:我们可以通过编写相关的python代码,让这些python代码表示一些行为动作,让这些行为动作出发到浏览器当中,让浏览器实现自动化的操作。
# 能不能让我的程序连接到浏览器 . 让浏览器来完成各种复杂的操作, 我们只接受最终的结果
# selenium: 自动化测试工具
# 可以: 打开浏览器. 然后像人一样去操作浏览器
# 程序员可以从selenium中直接提取网页上的各种信息
# 环境搭建:
#     pip install selenium -i 清华源
#     下载浏览器驱动:https://npm.taobao.org/mirrors/chromedriver
#         把解压缩的浏览器驱动 chromedriver 放在python解释器所在的文件夹

实战——爬取药监总局的动态加载数据

from selenium import webdriver
from lxml import etree
from time import sleep
# 实例化一个浏览器对象(前提要传出浏览器的驱动程序)
bro = webdriver.Chrome(executable_path='D:\python\Scripts\chromedriver.exe')
bro.get('http://scxk.nmpa.gov.cn:81/xk/')
# 获取浏览器当前页面的页面远吗数据
page_text = bro.page_source
# 解析企业名称
tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@id="gzlist"]/li')
for li in li_list:
    name = li.xpath('./dl/@title')[0]
    print(name)
sleep(5)
bro.quit()

实战——登录qq空间

from selenium import webdriver
import time
web = webdriver.Chrome()
web.get('https://qzone.qq.com/')
web.switch_to.frame('login_frame') #切换到新窗口
web.find_element_by_id("switcher_plogin").click()
userName_tag = web.find_element_by_id('u').send_keys('342954562') #输入账号
password_tag = web.find_element_by_id('p').send_keys('aedc22') #输入密码
time.sleep(1)
web.find_element_by_id('login_button').click()
time.sleep(10) #暂停10s
web.close() #关闭当前窗口

协程

  • 协程不是计算机提供的,是程序员任伟创造的。
  • 协程(Coroutine),也可以被称为微线程,是一种用户态的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行。

实现协程有这么几种方式:

  • greenlet(早期模块)
  • 
    02 协程~1 P84 - 08:25
    
  • yield关键字
  • 
    02 协程~1 P84 - 11:19
    
  • asyncio装饰器(python3.4引入的)
  • 
    02 协程~1 P84 - 15:19
    
  • async&await关键字(python3.5引入的)【推荐的】
  • 
    02 协程~1 P84 - 26:12
    

实战——爬取梨视频

 #需求:爬取梨视频的视频链接,并下载

#url="https://www.pearvideo.com/video_1763204"

url="https://www.pearvideo.com/video_1763507"

video_id = url.split("_")[1]

print(video_id)

#1、爬取源代码,看源代码中是否有视频链接

# 结果:搜索".mp4"关键字,没有搜到,视频链接不在源代码中;

#2、通过查看Network面板-> XHR -> 发送视频的链接地址

video_url = "https://www.pearvideo.com/videoStatus.jsp?contId="+video_id

#3. 向url发送get请求,得到视频的信息

import requests

response = requests.get(video_url, #游戏机的详细信息,详情页

  headers = {

    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36", #设置useragent

    "Referer": url #防盗链 京东首页

})

print(response.json()) #返回视频的信息,

#4、获取了视频的链接

srcUrl = response.json()['videoInfo']['videos']['srcUrl']

print(srcUrl) #404错误

# 将爬取的链接中的systemTime,替换成cont-video_id

systemTime = response.json()['systemTime']

print("系统时间:",systemTime)

newUrl = srcUrl.replace(systemTime, "cont-"+video_id)

print("新的链接:", newUrl)

# 爬取的链接:  https://video.pearvideo.com/mp4/adshort/20220524/1653618968522-15884803_adpkg-ad_hd.mp4

# 可以播放的链接:https://video.pearvideo.com/mp4/adshort/20220524/cont-1763204-15884803_adpkg-ad_hd.mp4

#5、进行视频的下载

video_content = requests.get(newUrl).content #视频的内容

with open("vido.mp4",mode="wb") as f:

  f.write(video_content)

print("结束!")




















2020年Python爬虫全套课程(学完可做项目)的评论 (共 条)

分享到微博请遵守国家法律