发布时间:2020/07/16 作者:天马行空 阅读(1356)
一、预备知识
爬虫是一段代码,它按照一定的规则,模拟人类上网的行为,自动地将互联网上的信息抓取下来,并存储到文件或者数据库中。
只要通过浏览器可以获取的数据和信息(包括文字、图片、音乐、视频等),爬虫都可以获取。
编写爬虫工具,除了需要比较牢固的掌握编程语言基础(比如Python)之外,对互联网的基础知识也需要有所了解。
1.1、统一资源定位符
爬虫爬取的对象是网络资源。如果把互联网比作一个城市,互联网中许许多多的网络资源就像是城市许许多多的住户。如果想要拜访某家住户,就必须知道这家的邮编街道门牌号。
协议加上网址,就是一个完整的统一资源定位符(Uniform Resource Locator),简称url。url是指每一网络资源在互联网上的唯一地址,有着统一的格式。
有时候,我们网址的开头并不是http,而是https。https是超文本传输安全协议的缩写,其中的s代表安全(secure)。
超文本传输协议是一种请求/响应式的协议。意思就是说,在这种协议下,当一个客户端(比如浏览器)和服务器建立连接后,会向服务器发送一个请求,请求以某种方式来获取服务器上的指定资源;在服务器接收到这个请求后,就会给客户端一个回应,给予相应的信息。这个过程,就是网络资源传输的过程。
超文本传输协议中定义了八种对网络资源的操作方式,分别是:GET,HEAD,POST,PUT,DELETE,OPTIONS,TRACE和CONNECT。不必记住这八种操作方法,只需要知道在使用爬虫时常用的操作是GET和POST就可以了。
有了统一资源定位符url,爬虫这个客户端/软件就可以通过向服务器发送一个http的GET请求获取网络资源了。这个网络资源是以超文本标记语言(html)格式表示的。
1.2、超文本标记语言
html是超文本标记语言(Hyper Text Markup Language)的缩写,电脑上的浏览器读取使用html语言写的文件,就可以呈现出网页五彩缤纷的样子。
html文档用不同的标签来组织内容,结构严格。而基于这种严格的结构,我们可以将它表现为树形结构。
1.3、Python实现爬虫
如何通过url找到对应的资源呢?又如何把html中的信息提取出来?
这就要用到Python的一些开源模块了,比如我们可以使用 requests 和 urllib 模块获取html文件;再使用 bs4 模块中的 BeautifulSoup类 把html文件中的内容提取出来。
二、发送网络请求发送网络请求
在文本传输协议下,想获取html文件,其实就是要让爬虫向服务器发送一个请求,这个过程叫做网络请求。网站的服务器会根据这个网络请求返回我们需要的数据。
在Python中,可以使用一个叫做requests的模块实现网络请求。因此,在代码中先使用语句import requests导入这个模块。
1.1、GET请求
在超文本传输协议下客户端可以向服务器发送八种操作请求,GET是其中的一种,它是向服务器索取数据的请求。在Python中,使用requests模块,GET请求对应的Python语句是requests.get(url)。
import requests
# 复制url并赋值给url变量
url = 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput='
# 发送get请求
req_result = requests.get(url)
# 打印请求结果
print(req_result.text)
1.2、POST请求
与GET不同,POST是一个发布请求。当客户端向服务器发出POST请求时,意味着我们在试图向服务器提交一些数据。比如这个代码例子中的提交职位查询请求。需要通过浏览器找到Referer-url及User-Agent,先通过Referer-url获得cookies
可以使用requests调用post()方法得到的结果对象的json()方法把这个字符串结果变成一个字典。
import requests # request url req_url = 'https://www.lagou.com/jobs/positionAjax.json' # headers my_headers={ 'Referer':'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36' } # 自动获得cookies s = requests.Session() s.get(my_headers["Referer"], headers=my_headers) cookies = s.cookies # request # 第一个查询参数,查询字符串 qs_params = {'needAddtionalResult':'false'} # 第二个查询参数,post字段 form_data = {'first':True,'kd':'python','pn':1} # 发送请求 req_result = requests.post(req_url, headers=my_headers, cookies=cookies, params=qs_params, data=form_data) # 打印请求结果的内容 print(req_result.text) print(req_result.json())
三、分析和提取信息
3.1、寻找关键信息
在使用Python提取内容之前,我们得先知道我们想找的内容在哪里,这样才能准确的提取我们需要的信息。
3.2、提取关键信息
确定了想要提取的关键信息之后,就可以使用for循环来遍历职位信息列表来进行输出了。
import requests # request url req_url = 'https://www.lagou.com/jobs/positionAjax.json' # headers my_headers={ 'Referer':'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=', 'User-Agent': 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0' } # 自动获取cookies s = requests.Session() s.get(my_headers["Referer"], headers=my_headers) cookies = s.cookies # 第一个查询参数 qs_params = {'needAddtionalResult':'false'} # 第二个查询参数 form_data = {'first':True,'kd':'python','pn':1} # 发送请求 req_result = requests.post(req_url, headers=my_headers, cookies=cookies, params=qs_params, data=form_data) # 将请求结果转换为字典 result = req_result.json() # 提取职位相关的信息并输出 positions_info = result["content"]["positionResult"]["result"] # for循环遍历职位列表并输出 for i in range(len(positions_info)): print("******第{}个Python相关职位的信息******".format(i+1)) print("职位名称:" + positions_info[i]["positionName"]) print("公司名称:" + positions_info[i]["companyShortName"]) print("薪资:" + positions_info[i]["salary"])
四、让爬虫动起来
4.1、爬取更多信息
import requests def crawler(form_data): # request url req_url = 'https://www.lagou.com/jobs/positionAjax.json' # headers my_headers = { 'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0' } # 自动获取cookies s = requests.Session() s.get(my_headers["Referer"], headers=my_headers) cookies = s.cookies # 第一个查询参数 qs_params = {'needAddtionalResult': 'false'} # 发送请求 req_result = requests.post(req_url, headers=my_headers, cookies=cookies, params=qs_params, data=form_data) # 将请求结果转换为字典 result = req_result.json() # 提取职位相关的信息并输出 positions_info = result["content"]["positionResult"]["result"] # 提取页码 pn = form_data['pn'] # for循环遍历职位列表并输出 for i in range(len(positions_info)): print("******第{}页第{}个Python相关职位的信息******" .format(pn, i + 1)) print("职位名称:" + positions_info[i]["positionName"]) print("公司名称:" + positions_info[i]["companyShortName"]) print("薪资:" + positions_info[i]["salary"]) # 循环5次,爬取5页 for n in range(1, 6): if n == 1: form_data = {'first': True, 'kd': 'python', 'pn': 1} crawler(form_data) else: # 在以下补充2-3行代码 # 实现:当页码数不为1时,设置form_data的实际参数值,并调用crawler方法 form_data = {'first': False, 'kd': 'python', 'pn': n} crawler(form_data)
4.2、间歇爬取
一种常用的反爬虫策略是监测发送请求的ip地址,如果同一个ip地址频繁的进行网络请求,那么这个地址也很容易被网站记住。一旦被记上小本本,那么来自这个ip地址的请求就会被拒绝。
为了解决ip地址频繁访问的问题,我们可以使用time.sleep()来控制访问节奏。也就是说,每爬取一页信息就让爬虫间隔一段时间。但注意间隔时间不要完全相同,因为如果控制了相同的时间,也有可能被服务器认定为一个爬虫。我们可以使用random.randint( )来随机获取间隔时长。
import requests import time import random # 定义一个函数用来更新cookies def cookies_update(url, my_headers): # 自动获取cookies s = requests.Session() s.get(url, headers=my_headers) cookies = s.cookies return cookies # 定义一个爬虫函数 def crawler(req_url, my_headers, cookies, qs_params, form_data): # 发送请求 req_result = requests.post(req_url, headers=my_headers, cookies=cookies, params=qs_params, data=form_data) # 将请求结果转换为字典 result = req_result.json() # 提取职位相关的信息并输出 positions_info = result["content"]["positionResult"]["result"] #提取页码 pn = form_data['pn'] # for循环遍历职位列表并输出 for i in range(len(positions_info)): print("******第{}页第{}个Python相关职位的信息******" .format(pn, i+1)) print("职位名称:" + positions_info[i]["positionName"] ) print("公司名称:" + positions_info[i]["companyShortName"] ) print("薪资:" + positions_info[i]["salary"] ) # 使用程序门卫 if __name__ == "__main__": # request url req_url = 'https://www.lagou.com/jobs/positionAjax.json' # headers my_headers = { 'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0' } qs_params = {'needAddtionalResult':'false'} form_data_n = {'kd':'python'} # 依次爬取5页数据 for n in range(1, 6): # 更新cookies,更新的cookies存储在updated_cookies变量中 updated_cookies = cookies_update(my_headers["Referer"], my_headers) # 每爬一页,随机间隔3-15秒 sleep_time = random.randint(3,15) time.sleep(sleep_time) form_data_n['pn'] = n if n == 1: form_data_n['first'] = True crawler(req_url, my_headers, updated_cookies, qs_params, form_data_n) else: form_data_n['first'] = False crawler(req_url, my_headers, updated_cookies, qs_params, form_data_n)
4.3、数据保存
import requests import time import random import pandas as pd # 定义一个函数用来更新cookies def cookies_update(url, my_headers): # 自动获取cookies s = requests.Session() s.get(url, headers=my_headers) cookies = s.cookies return cookies # 定义一个爬虫函数 def crawler(req_url, my_headers, cookies, qs_params, form_data): # 发送请求 req_result = requests.post(req_url, headers=my_headers, cookies=cookies, params=qs_params, data=form_data) # 将请求结果转换为字典 result = req_result.json() # 提取职位相关的信息并输出 positions_info = result["content"]["positionResult"]["result"] # for循环遍历职位列表并输出 # 定义一个字典保存每一页的职位信息 positions = { '职位名称': [], '公司名称': [], '薪资': [] } for i in range(len(positions_info)): positions["职位名称"].append(positions_info[i]["positionName"]) positions["公司名称"].append(positions_info[i]["companyShortName"] ) positions["薪资"].append(positions_info[i]["salary"] ) return positions if __name__ == "__main__": # request url req_url = 'https://www.lagou.com/jobs/positionAjax.json' # headers my_headers = { 'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0' } qs_params = {'needAddtionalResult': 'false'} #定义一个字典保存所有的职位信息 positions_all = { '职位名称': [], '公司名称': [], '薪资': [] } form_data = {'kd':'python'} for n in range(1, 6): print("爬取第{}页的数据".format(n)) updated_cookies = cookies_update(my_headers["Referer"], my_headers) form_data['pn'] = n if n == 1: form_data['first'] = True else: form_data['first'] = False positions_page = crawler(req_url, my_headers, updated_cookies, qs_params, form_data) positions_all['职位名称'] += positions_page['职位名称'] positions_all['公司名称'] += positions_page['公司名称'] positions_all['薪资'] += positions_page['薪资'] sleep_time = random.randint(3,15) time.sleep(sleep_time) result = pd.DataFrame(positions_all) result.to_excel('./tmp/Python_position.xlsx')
总结
爬虫:一段自动抓取互联网信息的程序或脚本。
反爬虫:通常网站的存在都是为了服务用户而不是服务爬虫,因为程序访问的频率高效益低,所以各大网站都会想办法判断发送请求的服务器是用户还是爬虫,如果是爬虫就会拒绝请求。
GET请求:超文本传输协议下,客户端向服务器发送的索取请求。
POST请求:超文本传输协议下,客户端向服务器发送的发布请求。
Json:一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
Python为爬虫提供的工具
1、requests包
为构建和发起网络请求提供基本功能。
requests.get():向服务器发起GET请求。
requests.post():向服务器发起POST请求。
requests.Session():获取连接相关的Session信息。可以将Session理解为客户端与服务器之间保持的一个契约,其中包含了cookie等用于辨别用户身份的信息。
2、time及random
为了防止被服务器的反扒功能屏蔽,我们需要借助random.randint()、time.sleep()产生人类访问网页服务时点击动作时间间隔不确定的特征。