1、爬虫基础

爬虫快速入门 / 2021-04-22

一、爬虫介绍

本节开始学习Python爬虫,这也是Python国内非常热火的应用场景之一。

1.1、爬虫简述

通过编写程序模拟浏览器上网,并用其在互联网上抓取网页数据的过程可称为爬虫。

1.2、爬虫合法性

爬虫只是一种技术手段,在法律中是不被禁止的!这就跟菜刀一样,本来是用来切菜的,你偏要用来砍人,这能不犯法吗!比如说

  • 非恶意爬虫

  • 如百度搜索、Google搜索,因为被搜索引擎收录可以增加自己网站和产品的曝光率,因此多数站点都乐意被搜索引擎爬取。所以说,只要是网页上公开显示的信息多数可以爬取。

  • 恶意爬虫

    • 如抢票软件,抢票软件或类似程序都是通过短时间内发起大量请求来抢夺服务器资源。

1.3、爬虫的风险

有了上面的恶意爬虫,那么就会延伸出一个风险,即法律风险,那么如何判断一个爬虫爬取数据的过程是否具有法律风险呢?这里简单罗列一下:

  • 干扰网站的正常运营

    • 这个好理解,就好像上面说的抢票软件一样,短时间内发起大量请求造成服务器堵塞,甚至瘫痪,这是不被允许的;再说了,你把目标爬宕机了还怎么获取数据!
  • 爬取受到法律保护的特信息

    • 这个不用过多解释了吧,你通过某种手段窃取到了机密,你说违不违法?

1.3、如何规避风险

我们使用爬虫是为了获取目标的数据,为了避免因为短时间内发起大量请求而导致目标网站的服务器瘫痪,我们需要对爬虫程序进行限制,避免干扰到网站的正常运营。

在使用或传播爬取到的数据前,审查抓取到的内容或者设定筛选机制,如有涉及到用户隐私或商业机密等敏感内容需要及时停止爬取并删除,其实在被人发现后无论你是否及时删除都已经造成了违法行为。。。

技术是一把双刃剑,好坏取决于使用者而不是技术本身!

1.4、爬虫分类

爬虫根据爬取内容的不同,有几种不同的爬取方式,这里简单罗列一下。

1.4.1、通用爬虫

这也是搜索引擎中抓取系统的重要组成部分,它抓取的是一整张HTML页面。

1.4.2、聚焦爬虫

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

1.4.3、增量式爬虫

检测网站中数据更新的情况,仅爬取网站中更新的数据。

二、补充知识

2.1、反爬

即门户网站通过制定相应技术手段,阻止爬虫程序对网站数据进行爬取。

2.2、防反爬

有反爬就有防反爬!爬虫程序可以通过指定相关的策略或手段,突破门户网站的反爬。

2.3、robots协议

一种专门存放于网站根目录下的txt文本文件(全称:robots.txt),它算是一种爬虫协议、互联网界的道德规范,建议人们按照这里面定义的哪些数据可被爬取,哪些不可以被爬取。

2.4、爬虫常用请求header

  • User-Agent:表示请求载体的身份标识,也就是客户端(多数是浏览器)的信息,如下所示:
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36
  • Connection:请求完后,是断开链接还是保持链接
Connection: keep-alive

2.5、爬虫常用响应header

  • Content-Type:服务器响应客户端的数据类型
Content-Type: text/html; charset=utf-8

三、Requests模块

requests是Python原生的一款基于网络请求模拟浏览器发起网络请求的模块,它功能强大、使用简单、效率高!

3.1、Requests请求流程

  1. 指定访问的URL
  2. 发起网络请求
  3. 获取服务器响应数据
  4. 持久化存储数据

3.2、环境的安装

pip3 install requests

3.3、Requests使用

由最基本的爬取开始学习

3.3.1、基本使用

下面以爬取百度首页为例演示requests的基本使用方法,请求流程参考3.1的requests请求流程。

import requests


url = 'http://www.baidu.com'  # 指定url
response = requests.get(url=url,)  # 发起GET请求并保存服务端响应数据
msg = response.text  # 将响应数据转换为字符串
with open('baidu.html', 'wt', encoding='utf-8') as file:  # 写入文件以持久化存储数据
    file.write(msg)
3.3.2、UA伪装
GET请求

UA即User-Agent,很多网站都有反爬措施,其中User-Agent绝对是首当其冲,我们需要伪装成浏览器来瞒天过海。

import requests

url = 'http://www.baidu.com'
# 将浏览器版本写入请求头,伪装成真实浏览器。
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36'
}
kwarg = input('please enter a word: ')  # 接收传入的参数来搜索
# wd是百度搜索的关键字,每个搜索引擎都可能不一样。
params = {
    'wd': kwarg
}
response = requests.get(url=url, params=params, headers=headers)
msg = response.text
with open('baidu.html', 'wt', encoding='utf-8') as file:
    file.write(msg)
POST请求

有时候我们的请求不一定是GET请求,也可能是POST请求,比如各类翻译平台需要我们输入文本,然后翻译平台展示翻译结果,这就得使用POST请求了,既然是POST请求就涉及到前后端交互,想起来了吗?要用JSON格式!

本次以百度翻译为例:

import requests
from json import dump


url = 'https://fanyi.baidu.com/sug'
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36'
}
kwarg = input('please enter a word: ')
data = {  # 百度翻译关键字是kw
    'kw': kwarg
}
response = requests.post(url=url, data=data, headers=headers)  # 用POST请求
msg = response.json()  # 必须提前摸清服务端返回数据类型,否则报错。
with open('fanyi.html', 'wt', encoding='utf-8') as file:
    dump(msg, fp=file, ensure_ascii=False)
3.3.3、批量爬取流程

以国家食品药品监督总局的化妆品生产许可信息管理系统服务平台为例,链接:http://scxk.nmpa.gov.cn:81/xk/

先爬为敬
import requests


url = 'http://scxk.nmpa.gov.cn:81/xk/'
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 '
                  'Safari/537.36 '
}

response = requests.get(url=url, headers=headers).text

with open('food.html', 'wt', encoding='utf-8') as file:
    file.write(response)

爬取完毕后,我们去看爬取的数据,会发现根本没有企业信息数据的,那这些企业信息数据又是怎么加载而来的?我猜测是自动加载得来,用F12抓包分析一下。

F12打开后,再次访问,会发现Network - xHR 有一个POST请求,我们想要的企业信息数据就是从这得来,先把它请求的格式拷贝下来,Request URL: http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList,centent,Content-Type: application/json;charset=UTF-8。

on: true
page: 1
pageSize: 15
productName: 
conditionType: 1
applyname: 
applysn: 

改进一下再爬取

import requests
from json import dump

url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 ' 'Safari/537.36 '
}
params = {
    'on': 'true',
    'page': 1,
    'pageSize': 15,
    'conditionType': 1
}
response = requests.post(url=url, data=params, headers=headers).json()

with open('food.json', 'wt', encoding='utf-8') as file:
    dump(response, fp=file)

把开存储的json文件,发现爬取的数据格式是这样而不是我们想要的企业详情信息,其中每个企业都有其独一无二的ID值,相信这里可以有价值可以挖掘。

{
	"filesize": "",
	"keyword": "",
	"list": [{
		"ID": "ed59438f34ae47e794f4c7ee5137c1f7",
		"EPS_NAME": "\u6d77\u5357\u4eac\u6da6\u73cd\u73e0\u751f\u7269\u6280\u672f\u80a1\u4efd\u6709\u9650\u516c\u53f8",
		"PRODUCT_SN": "\u743c\u598620160001",
		"CITY_CODE": "311",
		"XK_COMPLETE_DATE": {
			"date": 25,
			"day": 0,
			"hours": 0,
			"minutes": 0,
			"month": 3,
			"nanos": 0,
			"seconds": 0,
			"time": 1619280000000,
			"timezoneOffset": -480,
			"year": 121
		},
		"XK_DATE": "2026-04-25",
		"QF_MANAGER_NAME": "\u6d77\u5357\u7701\u836f\u54c1\u76d1\u7763\u7ba1\u7406\u5c40",
		"BUSINESS_LICENSE_NUMBER": "91460000294121210Y",
		"XC_DATE": "2021-04-25",
		"NUM_": 1
	}, 
}

我们再使用F12看看点击企业详情有没有POST请求,还真有,发现以下有价值数据,又是跟首页一样,是自动加载的POST请求。

Request URL: http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById
Request Method: POST
Content-Type: application/json;charset=UTF-8
id: ed59438f34ae47e794f4c7ee5137c1f7

如果批量获取多家企业的id后,就可以将id和url拼成完整的详情页对应的url了,这样就可以爬取所有的企业详情数据!

import requests
from json import dump

id_list = []
detail_list = []

url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
post_url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 '
                  'Safari/537.36 '
}

for page in range(6):  # 获取所有公司的ID,我这里只获取6页的公司。
    params = {
        'on': 'true',
        'page': page,
        'pageSize': 15,
        'conditionType': 1
    }

    company_id = requests.post(url=url, data=params, headers=headers).json()
    id_list.append(company_id)

for company in id_list:
    for detail in company.get('list'):
        data = {
            'id': detail.get('ID')
        }
        query_result = requests.post(url=post_url, data=data, headers=headers).json()
        detail_list.append(query_result)

with open('detail.json', 'wt', encoding='utf-8') as file:
    dump(detail_list, fp=file)
世间微尘里 独爱茶酒中