python学习第17节:网络爬虫实践

发布时间:2020/07/16 作者:天马行空 阅读(1021)

一、预备知识
爬虫是一段代码,它按照一定的规则,模拟人类上网的行为,自动地将互联网上的信息抓取下来,并存储到文件或者数据库中。
只要通过浏览器可以获取的数据和信息(包括文字、图片、音乐、视频等),爬虫都可以获取。
编写爬虫工具,除了需要比较牢固的掌握编程语言基础(比如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文档用不同的标签来组织内容,结构严格。而基于这种严格的结构,我们可以将它表现为树形结构。

图片.png


1.3、Python实现爬虫
如何通过url找到对应的资源呢?又如何把html中的信息提取出来?
这就要用到Python的一些开源模块了,比如我们可以使用 requests 和 urllib 模块获取html文件;再使用 bs4 模块中的 BeautifulSoup类 把html文件中的内容提取出来。

图片.png


二、发送网络请求发送网络请求
在文本传输协议下,想获取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())

图片.png


三、分析和提取信息

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"])

图片.png


四、让爬虫动起来

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')


图片.png


总结
爬虫:一段自动抓取互联网信息的程序或脚本。
反爬虫:通常网站的存在都是为了服务用户而不是服务爬虫,因为程序访问的频率高效益低,所以各大网站都会想办法判断发送请求的服务器是用户还是爬虫,如果是爬虫就会拒绝请求。
GET请求:超文本传输协议下,客户端向服务器发送的索取请求。
POST请求:超文本传输协议下,客户端向服务器发送的发布请求。
Json:一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。


Python为爬虫提供的工具
1、requests包
为构建和发起网络请求提供基本功能。
requests.get():向服务器发起GET请求。
requests.post():向服务器发起POST请求。
requests.Session():获取连接相关的Session信息。可以将Session理解为客户端与服务器之间保持的一个契约,其中包含了cookie等用于辨别用户身份的信息。

2、time及random
为了防止被服务器的反扒功能屏蔽,我们需要借助random.randint()、time.sleep()产生人类访问网页服务时点击动作时间间隔不确定的特征。

图片.png



关键字python python教程