0. 内容简介
这里,我们在工作中遇到了一个实际问题,即:
- 需要根据给出的淘宝网站链接获取网页中的sku标题以及主图链接信息。
借此机会,我们刚好来学习一下网页爬取相关的基础技能,然后来解决上述问题。
更进一步的,我们也学一下如何下载网页中的图片、视频等文件。
不过需要注意的是,这里,我们仅仅是作为一个学习性质的博文,内容也只是浅尝辄止,仅用于对工作所需功能的简单实现,并于大家进行分享和交流。
但如果后续有读者基于本文进行了深度地学习,并用相关技术引起了相关的法律问题,请恕本文概不对其进行负责。
1. 网页信息获取
首先,我们来看如何来获得网页信息。
1. 莫烦教程方法
在参考链接1中莫烦的视频教程中,他使用urllib库的urllib.request.urlopen
方法进行网页内容的爬取。
具体命令为:
from urllib.request import urlopen
html = urlopen("https://detail.tmall.com/item.htm?spm=a230r.1.14.24.7acb2075Uiwtjj&id=601871231483&ns=1&abbucket=20").read()
但是在实际操作中,我们发现其中有许多的坑,主要包括:
- 两次爬取命令运行就遇到了证书问题导致第二次获取网页内容失败,出现下述报错:
URLError: <urlopen error [SSL: TLSV1_ALERT_INTERNAL_ERROR] tlsv1 alert internal error (_ssl.c:748)>
- 对html内容进行解码时,由于网页不一定按照
utf-8
进行编码,因此,html.decode("utf-8")
命令可能会出现如下报错:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 1742: invalid start byte
后续咨询了公司里面的数据组的朋友,发现前者的原因大概率是由于被网页判断为了爬虫而被屏蔽了操作,后者则是由于网页内容不支持utf-8
编码。
更好地获取网页内容的方式为使用requests加上header信息的方式进行数据的爬取。
2. header信息获取
在普通的urlopen方法中,我们获取的就是普通的流信息,而无法知道http信息中的内容的编码方式等信息,因此,就会出现上述解码不知道该用什么方式解码的情况。
要做到这一点,我们需要在请求url的时候预先知道相关网站的头信息,这样,我们才能够在后续的操作中顺利地对网页内容进行解码和分析。不过,更一般的情况下,我们事实上是在请求的过程中直接带入头信息,从而做到在读取网页信息的同时就进行网页内容的解析。
因此,我们就需要考察一下如何来获取网页请求的头信息。
获取头信息的方式我们可以通过获取网页的请求curl命令然后通过网上的转换工具(比如参考链接6中的网站工具)来直接获得请求命令。
我们打开淘宝的网站链接,用F12快捷键打开开发者工具栏,刷新之后获得的第一个请求就是该网站的直接的请求命令。
右键该链接然后复制其curl地址,我们就可以获得cmd命令行中直接请求的命令,然后使用上述在线转换工具中转换为python代码即可。
转换之后我们就可以获得相应的request代码,我们只取其中的头信息headers
内容,删除其中诸如cookies等无用信息后给出样例如下:
headers = {
'authority': 'detail.tmall.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
'referer': 'https://s.taobao.com/',
'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8',
}
3. 使用requests获取网页内容
最后,我们来看如何来获取网页内的内容。
在莫烦的视频中,网页信息获取之后是一个数据流,我们首先要通过read()
方法获取其内容,然后通过decode
方法将其转换为可读的编码(如果其中存在中文内容的话)。
如果是按照莫烦教程中使用urllib
中的urlopen
方法的话,那么我们还需要手动通过read
以及decode
函数对其进行内容的读取,有点类似于python文件的读取方式。
但是,如果采用requests加头信息的方式的话,上述过程事实上已经在头信息中配置完成了,因此,我们直接获得的结果就是我们可读的结果。
事实上,上述curl转换python的工具中本就会生成requests的调用请求。我们给出其调用代码如下:
import requests
headers = {
'authority': 'detail.tmall.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
'referer': 'https://s.taobao.com/',
'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8'
}
params = (
('id', '629648945951'),
)
response = requests.get('https://detail.tmall.com/item.htm', headers=headers, params=params)
或者,我们可以更暴力一些,直接不通过params传入参数,直接将网页的url链接传递进去。
import requests
headers = {
'authority': 'detail.tmall.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
'referer': 'https://s.taobao.com/',
'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8'
}
response = requests.get('https://detail.tmall.com/item.htm?spm=a230r.1.14.24.349e20750ClDZd&id=601871231483&ns=1&abbucket=20', headers=headers)
这样,我们就能够直接获取到网页中的内容信息。
4. 网页爬取失败原因考察
实际在测试中,我们发现,即使使用头信息的方式,我们依然遇到了上述请求过于频繁导致的问题。
下面,我们简单对其进行一下分析,看看能否通过什么方式绕开这个问题。
后续问了一下做数据的同事,发现这个问题并不是一个好解的问题,本质原因还是在于网页的反爬机制,能够被发现是爬虫信息的原因在于python的request请求行为与浏览器中实际发生的请求行为不一致。
具体而言,在浏览器中,每一次打开网页事实上都会触发大量的相关网页的请求,且浏览器的请求中cookie信息会随浏览行为的发生而发生改变,而request发送的请求中cookie信息往往是固定的,这就导致网页可以由此发现请求到底来源于代码还是用户的实际浏览器行为。
后续当然不是说没有策略绕开这些反爬机制,但是整体来说这是一个和网站设计者斗智斗勇的过程,这里仅仅作为一个普普通通的简介性质的博文,就没有必要研究的那么深了。。。
2. 网页信息解析
下面,我们来考察网页信息的解析方法。
我们采用BeautifulSoup工具进行网页内容的解析。
BeautifulSoup算是一个针对网页信息的高级版正则表达式封装,我们可以不用写复杂的正则匹配规则直接使用BeautifulSoup中的内置方法进行网页信息的获取。
1. BeautifulSoup的安装
首先,我们快速地给出以下BeautifulSoup的安装过程,这个其实只需要pip安装一下就行了,唯一需要注意的是,BeautifulSoup库的pip包名称为beautifulsoup4
,其他都没啥区别。
安装完成之后,导入的方式也需要注意一下,BeautifulSoup的导入方式为:
from bs4 import BeautifulSoup
2. BeautifulSoup的使用
现在,我们来看一下BeautifulSoup的具体使用。
要详细说明其使用方法,我们需要首先来看一下网页中信息内容的结构。
一般来说,网页中的信息都会长这个样子:
<meta name="keywords" content="花花公子男装夹克男春季新款休闲冲锋衣连帽宽松潮流短款男士外套"/>
要获取其中的信息,BeautifulSoup的语法为:
soup = BeautifulSoup(html)
skutitle = soup.find("meta", {"name": "keywords"})["content"]
其中,meta为文件树的信息字段,后面的参数为过滤条件,最后取出content字段的内容。
同样的,我们可以快速地得到,获取淘宝商品首图的python命令为:
image = soup.find("img", {"id": "J_ImgBooth"})["src"]
3. 网页中文件的下载
最后,我们来看一下如何从网页中下载文件,比如说,如何获取上述获得的图片。
给出获得的图片的url链接为:
url = "https://img.alicdn.com/imgextra/i4/1851041537/O1CN01qd5ZSB1NDzO4pNexv-1851041537.jpg_430x430q90.jpg"
这部分有两种实现方式:
- 一种实现方式是使用wget库将其作为一个文件进行下载;
- 第二种是将其作为数据流进行读取,然后写入到一个文件当中。
下面,我们分别来对其进行考察:
1. 将网页中文件作为数据流进行读取后写成一个文件
给出代码样例如下:
import requests
url = 'https://img.alicdn.com/imgextra/i4/1851041537/O1CN01qd5ZSB1NDzO4pNexv-1851041537.jpg_430x430q90.jpg'
with open("image.jpg", "wb") as fp:
r = requests.get(url)
fp.write(r.content)
如此,我们即可从原始链接中获取图片文件。
2. 使用wget方式直接进行文件下载
如果在bash命令下,如果我们要获取上述网络图片,我们只需要使用下述命令即可:
wget https://img.alicdn.com/imgextra/i4/1851041537/O1CN01qd5ZSB1NDzO4pNexv-1851041537.jpg_430x430q90.jpg image.jpg
同样的,在python中,同样有一个wget库,可以快速实现文件的下载:
import wget
wget.download(url, "image.jpg")