Python应用:打造一个简单的爬虫

文章目录

  • 爬虫基本步骤
  • 如何发起网页请求
    • requests的安装
    • requests的使用
  • 如何解析拿到的HTML
  • 反爬虫与反反爬虫机制
  • 进阶
    • http请求头
      • User-Agent
  • 参考文献

爬虫基本步骤

对于一般的爬虫而言,其基本步骤:

  1. 找到需要爬取内容的网页URL;
  2. 打开该网页的检查页面,即F12查看HTML代码;
  3. 在HTML中挑选你想提取的数据;
  4. 通过代码进行网页请求(将html文件下载到本地)、解析(从html中自动解析出你想要的数据)
  5. 存储数据。

爬虫是典型的入门门槛低,但进阶难度高,进阶的反反爬虫的难度能玩死人。

爬虫有哪些工具呢?或者说有哪些框架呢?

很多。

比如说:

Scrapy框架,这是一个比较成熟的python爬虫框架,可以高效爬取各种web页面并提取结构化信息;

同样的还有Crawley框架、据说是可以允许没有编程基础的用户可视化爬取网页的Portia框架、专门用来提取新闻和文章以及内容分析的newspaper框架等等等。

其中最流行的框架,莫过于Scrapy框架,建议的确有爬虫想法的同学可以试着学习这个。

但是这里我们不讲,因为入门只需要一个requests+bs4就可以。requests库对新手真的是很友好,可以供新手做一些简单的任务,但是真正的大批量的爬虫,这个可能就不合适了。最大的原因是,requests是同步,而非异步,所以它在http请求的时候,IO会卡住,直到网站返回,所以会让整体的爬取速度变慢。

那有没有支持异步的类requests库呢?

有的。

比如说纯异步框架aiohttp、asks库、vibora等。

不过这里为了讲解的方便,以及节省其他的学习成本,下面我们以传统的requests+bs库来做爬虫的爬取和解析。

如何发起网页请求

最常用的是requests库,一个非常流行的http请求库。这里请求的是网页的html信息。

服务器收到请求后,会返回相应的网页对象。

Python应用:打造一个简单的爬虫_第1张图片

requests的安装

首先我们需要安装requests库:

pip install requests

requests的使用

然后我们来简单使用一下,访问一个网址,并拿回它响应的html文件,即结构化数据。

import requests

# 设置请求头信息
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}

r = requests.get(
    url='https://www.taikang.com/',
    headers=headers
)

# 设置编码格式
r.encoding='utf8'

# 输出响应内容
print(r.text)

如何解析拿到的HTML

再上一步中,我们已经通过requests,拿到了响应的HTML文档,我们需要的信息都在这个HTML文档里。

那如何从这里抽取出我们需要的信息呢?

你需要一个XML文档的解析包,python中有很多这种库包,比如说xpath,比如说beautifulsoup4。

没有比较过这些解析包的优劣,我很早之前学爬虫的时候,选择的是beautifulsoup4,即bs4。

pip install beautifulsoup4

bs4的完整使用,可以见参考文献3中的bs4官方文档,这里只介绍简单的使用。

以上一步中拿到的html为例,假设我想得到网站中的下面数据:

Python应用:打造一个简单的爬虫_第2张图片

当然,首先我需要在html中定位,看这几个数字位于html哪个标签下,经过探查,发现是在这里:

Python应用:打造一个简单的爬虫_第3张图片

好的,接下来就是一级一级解析了,我需要从body标签一级一级往下,一层一层的剥开div,找到这个数字。

话不多少,以下是代码:

import re

import requests
from bs4 import BeautifulSoup

# 设置请求头信息
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}

r = requests.get(
    url='https://www.taikang.com/',
    headers=headers
)

# 设置编码格式
r.encoding='utf8'

# 输出响应内容
# print(r.text)

# 开始解析HTML
soup = BeautifulSoup(r.text, 'lxml')
# bs的标签过滤器
# res = soup.find_all('p', class_='tk_hxneirong')
# res = soup.find_all('div', class_='tk_hxboxcon1')
res = soup.find_all('div', class_=re.compile('tk_hxboxcon[0-9]'))   # 模糊匹配

for r in res:
    print('-----')
    # print(r)
    if len(r.get_text()) > 0:   # get_text拿到tag以及子孙tag中的文本内容
        # print(r.get_text().strip('\r\n'))
        print(r.get_text().replace('\n', '').replace('\r', '')) # 删掉回车和换行,win下用\r\n换行,linux用\n
# print(res)

Python应用:打造一个简单的爬虫_第4张图片

上面是结果,预期目标成功完成。

反爬虫与反反爬虫机制

网站为了防止自身的数据被随意抓取,造成信息泄露或者服务器无用压力,会进行一系列的措施来使得本身数据不易被别的程序爬取,这些措施就是反爬虫。

比如检测IP访问频率、监控资源访问速度、请求的链接是否带有关键参数、验证码检测、ajax混淆、js加密等等。

对于目前市场上的反爬虫,爬虫工程师常用的反反爬虫方案主要有:

  • 不断试探目标底线,试出单IP下最优的访问频率(有的网站,对于单位时间内访问量超过阈值的IP,是直接做封禁处理的);
  • 构建自己的IP代理池;
  • 维护一份自己常用的UA库;
  • 针对目标网页的Cookie池;
  • 需要JS渲染的网页使用无头浏览器进行代码渲染再抓取;
  • 一套破解验证码程序;
  • 扎实的JS知识来破解混淆函数

爬虫和反爬虫永远处于一个此起彼伏此消彼长的博弈状态:

Python应用:打造一个简单的爬虫_第5张图片

上图忘记是从哪儿下的了

通过User-Agent来控制访问

无论是浏览器还是爬虫程序,在向服务器发起网络请求的时候,都会发过一个请求头文件,来表明自己的身份。

一个请求头由很多元素组成,最重要的就是User-Agent。

很多网站都会建立自己的User-Agent白名单,只有属于正常范围的user-agent才能进入访问。

通过JS脚本加密来防止爬虫

原理是什么呢?

举个例子,在发起请求之前,网站会通过js代码生成一大堆随机的数字,然后要求浏览器通过js的运算得到这一串数字的和,再返回给服务器。

解决方法:使用PhantomJS

* PhantomJS是一个Python包,他可以在没有图形界面的情况下,完全模拟一个”浏览器“,js脚本验证什么的再也不是问题了。

不过好像PhantomJS现在已经停止更新了

通过IP限制来反爬虫

如果一个固定的ip在短暂的时间内,快速大量的访问一个网站,那自然会引起注意,管理员可以通过一些手段把这个ip给封了,爬虫程序自然也就做不了什么了。

解决方法:

比较成熟的方式是:IP代理池

简单的说,就是通过ip代理,从不同的ip进行访问,这样就不会被封掉ip了。不过IP代理的获取比较麻烦,网上倒是有免费渠道,但是质量都不咋地。

通过单位时间内访问次数来反爬虫

有规律的sleep进程就可以。

比如随机1-3秒爬一次,爬10次修复10s,且每天只在8-12和18-20点之间爬,隔几天还休息一下,旨在尽力混淆人和爬虫。

对付这种爬虫,稍微有点麻烦,可以采用时间窗口+阈值的方式来反爬虫,比如说3个小时内总请求次数超过50次就禁止该IP的访问,或者是弹出验证码之类的。

验证码机制来反爬虫

比如说之前说的12306的图片验证码机制,不过现在的打码平台+机器学习已经很成熟了,这种方式也不保险了。

或者是干脆使用带验证码登录的cookie来绕过登录,直接在web上登陆之后取下cookie并保存然后带上cookie做爬虫,但这不是长久的方法,因为cookie隔一段时间会失效。

所以,反爬虫的结论是:在爬虫与反爬虫的对弈中,爬虫一定会胜利。因为只要人类能够正常访问的网页,爬虫在具备同种资源的情况下就一定可以抓取到(比如说基于selenium)。

进阶

http请求头

什么是http请求头?

当你使用http或者https协议请求一个网站的时候,你的浏览器会向对方的服务器发送一个http请求报文,这个请求报文由三部分组成:

请求行 + 请求头 + 请求体

Python应用:打造一个简单的爬虫_第6张图片

下面是一个请求头的详细介绍

需要注意的是,不同网页的请求头参数数量是不一样的。

Python应用:打造一个简单的爬虫_第7张图片
上图出处忘了。。

User-Agent

user-agent是做什么的呢?

user-agent会告诉网站服务器,访问者是通过什么工具来请求的,是爬虫还是用户浏览器。有的网站会直接拒绝爬虫请求,而只应答用户浏览器请求。所以我们在写爬虫的时候,需要伪造一个请求头来欺骗网站。

在requests中,如果不显式构造请求头的话,那我们写的爬虫请求会老老实实的告诉服务器:我是一个python爬虫请求。

import requests

r = requests.get(
    url='https://www.taikang.com/'
)
# 输出默认的请求头
print(r.request.headers)

输出:

{'User-Agent': 'python-requests/2.27.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

所以,我们最好伪装出一个请求头。

如何查看自己浏览器的请求头呢

很简单。

  1. 打开要爬虫的网页;
  2. F12打开开发者界面;
  3. 按F5刷新网页;
  4. 点击Network,随意点击一个requests;
  5. 点击Headers,查看Request Headers的User-Agent字段,直接复制
  6. 将刚才复制的User-Agent字段构造成字典形式。

于是一个请求头构造完毕。

Python应用:打造一个简单的爬虫_第8张图片

User-Agent各部分代表了什么意思呢?

简单的讲,

Mozilla/5.0 (平台) 引擎版本 浏览器版本号

第一部分的Mozilla,是历史遗留问题。由于历史上的浏览器大战,当时想获得图文并茂的网页,就必须宣称自己是 Mozilla 浏览器。此事导致如今User-Agent里通常都带有Mozilla字样,出于对历史的尊重,大家都会默认填写该部分。

第二部分表示平台

这部分由多个字符串组成,用英文半角分号分开。

Windows NT 10.0表示使用的操作系统版本,Win64;x64表示操作系统是64位的。

Windows NT 5.0 // 如 Windows 2000 
Windows NT 5.1 // 如 Windows XP
Windows NT 6.0 // 如 Windows Vista 
Windows NT 6.1 // 如 Windows 7
Windows NT 6.2 // 如 Windows 8
Windows NT 6.3 // 如 Windows 8.1
Windows NT 10.0 // 如 Windows 10
Win64; x64 // Win64 on x64
WOW64 // Win32 on x64

Linux系统下:

X11; Linux i686; // Linux 桌面,i686 版本
X11; Linux x86_64; // Linux 桌面,x86_64 版本
X11; Linux i686 on x86_64 // Linux 桌面,运行在 x86_64 的 i686 版本

macos下:

Macintosh; Intel Mac OS X 10_9_0 // Intel x86 或者 x86_64
Macintosh; PPC Mac OS X 10_9_0 // PowerPC
Macintosh; Intel Mac OS X 10.12; // 不用下划线,用点

第三部分表示引擎版本

AppleWebKit/537.36 (KHTML, like Gecko)…Safari/537.36

为什么写成这样,原因很复杂。

历史上,苹果依靠webkit内核开发出了Safari浏览器,WebKit内核包含了WebCore引擎,而WebCore引擎又是从KHTML衍生而来。

同时由于历史原因,KHTML引擎在使用的时候,必须声明自己是类似 Gecko的,因此写成了“like Gecko”。

再后来,Google开发Chrome也使用了WebKit内核,于是也跟着这么写了。

再再后来,Chrome希望自己能得到为Safari编写的网页,于是决定装成Safari,这就是为什么引擎版本里有Safari的版本。

一句话来讲,Chrome伪装成Safari,而Safari使用了WebKit渲染引擎,而WebKit是从KHTML引擎衍生而来的,而KHTML引擎必须声明自己是like Gecko 引擎。同时,所有的浏览器必须宣称自己是Mozilla。

不过,后来Chrome 28某个版本改用了blink内核,但还是保留了这些字符串。而且,最近的几十个版本中,这部分已经固定,没再变过。

第四部分是浏览器版本

这个没什么好讲的,我用的就是chrome嘛。

最后,竟然有大佬已经封装了不同操作系统不同浏览器的User-Agent,见参考文献2。

from fake_useragent import UserAgent
ua = UserAgent()

ua.ie
# Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US);
ua.msie
# Mozilla/5.0 (compatible; MSIE 10.0; Macintosh; Intel Mac OS X 10_7_3; Trident/6.0)'
ua['Internet Explorer']
# Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .NET CLR 3.3.69573; WOW64; en-US)
ua.opera
# Opera/9.80 (X11; Linux i686; U; ru) Presto/2.8.131 Version/11.11
ua.chrome
# Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2'
ua.google
# Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1290.1 Safari/537.13
ua['google chrome']
# Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11
ua.firefox
# Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1
ua.ff
# Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1
ua.safari
# Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25

# and the best one, random via real world browser usage statistic
ua.random

参考文献

  1. HTTP请求头之User-Agent 写的非常棒
  2. github-fake-useragent 竟然已经有大佬把请求头给封装了
  3. Beautiful Soup 4.4.0 中文文档
  4. 如何应对网站反爬虫策略?如何高效地爬大量数据? - fireling的回答 - 知乎
  5. [Python有哪些常见的、好用的爬虫框架? - 麻瓜编程的回答 - 知乎][https://www.zhihu.com/question/60280580/answer/617068010]
  6. Python有哪些常见的、好用的爬虫框架? - 小小造数君的回答 - 知乎 介绍了常见的python爬虫框架,并做了使用上的简单介绍。
  7. 超详细教程:什么是HTTP请求头/响应头 对请求头和响应头描述的比较详细,可能开头写的不是很好,对请求报文的理解,推荐看下一篇
  8. HTTP请求报文(请求行、请求头、请求体)

你可能感兴趣的:(Python,python,爬虫,开发语言)