爬虫技术的简单应用与实现

前言

  随着深度学习的不断发展,越来越多的研究者们转向研究深度学习,在研究的过程中需要大量的数据训练模型以及模型的预测。这些数据在很多时候是通过爬虫的形式将其保存在本地。爬虫分为聚焦爬虫、通用爬虫以及分布式爬虫。今天,我们给大家介绍的是爬虫比较简单的应用与实现。通过一个案例来说明我们爬虫的过程以及爬虫用到的一些网络中的基础。

一、爬虫基础

  我们在爬虫的过程中,首先是模拟我们上网查找资料的过程对网页进行访问。接下来给大家介绍正常的我们访问网页的过程,只有在清楚我们学习者是如何去找到网页,即上网的过程,才可以通过代码用机器模拟的形式爬取相应的资源。一般来讲,我们首先在网页中输入自己请求的内容,也就是说我们要进行HTTP请求,这个过程要求我们在地址栏输入网址,然后通过网络请求到该页面,具体如图所示:

  http的请求方式有get和post请求:

  • 1、get请求:
    (1)、比较便捷
    (2)、它的传输是一种明文的,因此,安全性得不到保障。当然它的长度也会受到限制。
  • 2、post的请求方式
    (1)、比较安全
    (2)、数据的整体没有限制
    (3)、可以上传文件的。
  • 3、put是一种不完全的方式。
  • 4、delete是用来删除信息
  • 5、head是请求头信息

  但是,一般而言,我们用的最多的就是前两种请求方式,即:get或者post请求。具体如下所示:

  由图中可知,我们除了请求方式之外,还有一些请求头的相关信息。我们在发送网络请求的时候,需要带有一定的数据给服务器,当然不带也可以,正如上图所示,请求头里包含了request、header;在返回数据的时候也有相应的response。以下是请求头的一些常用的信息:

  • 1、Accept:文本的格式
  • 2、Accept-encoding:编码格式
  • 3、Connection:长链接、短链接
  • 4、Cookie:验证用的
  • 5、Host:域名
  • 6、Referer:标志从那个页面跳转过来的
  • 7、User-Agent:浏览器和用户的信息

  我们现在是一个数据的时代,很多时候,通过数据分析可以得到背后隐藏的信息或者存在的一些商机。我们可以通过爬虫将爬取到的数据进行数据分析,做出合理的分析报告;其实我们在分析数据的时候,很多时候都是在进行爬取我们需要的数据。一般通用的就是爬取百度或者google相应的数据,这些数据通常的公开的,爬取的难度小,当然,爬取的速度也是挺快的,但是,也有一定的劣势。最为明显的就是爬取数据没有针对性,会爬取很多我们不需要的数据。因此,我们一般用于研究的话,我们爬取的方式是聚焦爬虫。这种爬虫方式针对性较强,目标明确,对我们的需求定位也是比较精准的,返回的内容是很固定的,一般为增量式:即从第一页请求到最后一页。Deep深度爬虫:一般爬取的是静态数据,一般包括网页的html、css等还有就是动态数据,包括js代码以及加密的js。最后,我们给大家介绍爬虫的工作原理:

  • 1、需要使用者确定爬取的url,这里需要我们在网页中去找到相应的url
  • 2、使用python或者java代码发送请求获取数据,不过本文用的是python
  • 3、解析获取到的数据;这里需要我们精确找到数据

二、爬虫实践

  我们首先通过一个简单的案例对上述的爬虫原理进行简单的应用,这个案例是对百度进行简单的爬取;具体代码如下:

import urllib.request
def load_data():
    url = "http://www.baidu.com/"
    # get的请求
    # http请求
    # response:http相应的对象
    response = urllib.request.urlopen(url)
    print(response)
    # 读取内容:byte
    data = response.read()
    print(data)
    # 将文件获取的内容转换为字符串
    str_data = data.decode("utf-8")
    print(str_data)
    # 将数据写入文件
    with open("baidu.html", "w", encoding='utf-8') as f:
        f.write(str_data)
    # 将字符串转换为bytes类型
    str_name = "baidu"
    bytes_name = str_name.encode("utf-8")
    print(bytes_name)
    # python的爬取类型:str、bytes
    # 如果爬取的类型是bytes,但是写入的是需要字符串, decode("utf-8")
    # 如果爬取的类型是str,但是写入的是需要bytes, encode("utf-8")
load_data()

  具体的实现效果如图所示:

  当然我们还生成了一个baidu.html的文件,打开之后和我们的控制台返回的内容是一致的;具体如下:

  接下来,给大家介绍有返回参数的爬虫实现,具体实现如下:

import urllib.request
import urllib.parse
import string
def get_method_params():
    # 拼接字符串(汉字)
    url = "http://www.baidu.com/s?wd="
    # python可以接触的数据
    # https://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3
    # name = "美女"
    idol = "刘涛"
    final_url = url + idol
    print(final_url)
    # 使用代码发送网络请求
    # 网址里面包含了汉字;ASCII码没有汉字的;URL转义
    # 使用代码发送网络请求
    # 将包含汉字的网址进行转义
    encode_new_url = urllib.parse.quote(final_url, safe=string.printable)
    print(encode_new_url)
    response = urllib.request.urlopen(encode_new_url)
    print(response)
    # 读取内容:
    data = response.read().decode()
    print(data)
    # 保存到本地
    with open("02-encode.html", "w", encoding="utf-8") as f:
        f.write(data)
    # UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-11: ordinal not in range(128)
    # python是解释性语言;解析器只支持acsii 0 - 127
    # 不支持中文
get_method_params()

  具体的实现效果如图所示:

  当然我们还生成了一个encode.html的文件,打开之后和我们的控制台返回的内容是一致的;具体如下:

  最后,我们通过对《乘风破浪的姐姐》的爬取,对数据进行简单的分析。首先在爬取之前我们应该在后台通过pip安装bs4,具体安装命令如下:

pip install Beautifulsoup4 -i https://pypi.douban.com/simple

  安装过程如下:

  安装之后,我们通过以下命令验证是否安装成功,如果不报错即成功:

import bs4

  具体结果如下

  接下来,我们就将必要的库导进去,然后对《乘风破浪的姐姐》进行数据的爬取,具体代码实现如下:

import json
import re
import requests
import datetime
from bs4 import BeautifulSoup
import os


def crawl_wiki_data():
    """
    爬取百度百科中《乘风破浪的姐姐》中嘉宾信息,返回html
    """
    headers = {
    
    
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    }
    url = 'https://baike.baidu.com/item/乘风破浪的姐姐'

    try:
        response = requests.get(url, headers=headers)
        # 将一段文档传入BeautifulSoup的构造方法,就能得到一个文档的对象, 可以传入一段字符串
        soup = BeautifulSoup(response.text, 'lxml')

        # 返回所有的<table>所有标签
        tables = soup.find_all('table')
        crawl_table_title = "按姓氏首字母排序"
        for table in tables:
            # 对当前节点前面的标签和字符串进行查找
            table_titles = table.find_previous('div')
            for title in table_titles:
                if (crawl_table_title in title):
                    return table
    except Exception as e:
        print(e)


def parse_wiki_data(table_html):
    '''
    解析得到选手信息,包括包括选手姓名和选手个人百度百科页面链接,存JSON文件,保存到work目录下
    '''
    bs = BeautifulSoup(str(table_html), 'lxml')
    all_trs = bs.find_all('tr')

    stars = []
    for tr in all_trs:
        all_tds = tr.find_all('td')  # tr下面所有的td

        for td in all_tds:
            # star存储选手信息,包括选手姓名和选手个人百度百科页面链接
            star = {
    
    }

            if td.find('a'):
                # 找选手名称和选手百度百科连接
                if td.find_next('a'):
                    star["name"] = td.find_next('a').text
                    star['link'] = 'https://baike.baidu.com' + td.find_next('a').get('href')

                elif td.find_next('div'):
                    star["name"] = td.find_next('div').find('a').text
                    star['link'] = 'https://baike.baidu.com' + td.find_next('div').find('a').get('href')
                stars.append(star)

    json_data = json.loads(str(stars).replace("\'", "\""))
    with open('F:/python_program/Paddle_Training/OneDay_basicKnowledge/' + 'stars.json', 'w', encoding='UTF-8') as f:
        json.dump(json_data, f, ensure_ascii=False)


def crawl_everyone_wiki_urls():
    '''
    爬取每个选手的百度百科图片,并保存
    '''
    with open('F:/python_program/Paddle_Training/OneDay_basicKnowledge/' + 'stars.json', 'r', encoding='UTF-8') as file:
        json_array = json.loads(file.read())
    headers = {
    
    
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    }
    star_infos = []
    for star in json_array:
        star_info = {
    
    }
        name = star['name']
        link = star['link']
        star_info['name'] = name
        # 向选手个人百度百科发送一个http get请求
        response = requests.get(link, headers=headers)
        # 将一段文档传入BeautifulSoup的构造方法,就能得到一个文档的对象
        bs = BeautifulSoup(response.text, 'lxml')
        # 获取选手的民族、星座、血型、体重等信息
        base_info_div = bs.find('div', {
    
    'class': 'basic-info cmn-clearfix'})
        dls = base_info_div.find_all('dl')
        for dl in dls:
            dts = dl.find_all('dt')
            for dt in dts:
                if "".join(str(dt.text).split()) == '民族':
                    star_info['nation'] = dt.find_next('dd').text
                if "".join(str(dt.text).split()) == '星座':
                    star_info['constellation'] = dt.find_next('dd').text
                if "".join(str(dt.text).split()) == '血型':
                    star_info['blood_type'] = dt.find_next('dd').text
                if "".join(str(dt.text).split()) == '身高':
                    height_str = str(dt.find_next('dd').text)
                    star_info['height'] = str(height_str[0:height_str.rfind('cm')]).replace("\n", "")
                if "".join(str(dt.text).split()) == '体重':
                    star_info['weight'] = str(dt.find_next('dd').text).replace("\n", "")
                if "".join(str(dt.text).split()) == '出生日期':
                    birth_day_str = str(dt.find_next('dd').text).replace("\n", "")
                    if '年' in birth_day_str:
                        star_info['birth_day'] = birth_day_str[0:birth_day_str.rfind('年')]
        star_infos.append(star_info)

        # 从个人百度百科页面中解析得到一个链接,该链接指向选手图片列表页面
        if bs.select('.summary-pic a'):
            pic_list_url = bs.select('.summary-pic a')[0].get('href')
            pic_list_url = 'https://baike.baidu.com' + pic_list_url

        # 向选手图片列表页面发送http get请求
        pic_list_response = requests.get(pic_list_url, headers=headers)

        # 对选手图片列表页面进行解析,获取所有图片链接
        bs = BeautifulSoup(pic_list_response.text, 'lxml')
        pic_list_html = bs.select('.pic-list img ')
        pic_urls = []
        for pic_html in pic_list_html:
            pic_url = pic_html.get('src')
            pic_urls.append(pic_url)
        # 根据图片链接列表pic_urls, 下载所有图片,保存在以name命名的文件夹中
        down_save_pic(name, pic_urls)
        # 将个人信息存储到json文件中
        json_data = json.loads(str(star_infos).replace("\'", "\""))
        with open('F:/python_program/Paddle_Training/OneDay_basicKnowledge/' + 'stars_info.json', 'w', encoding='UTF-8') as f:
            json.dump(json_data, f, ensure_ascii=False)

def down_save_pic(name,pic_urls):
    '''
    根据图片链接列表pic_urls, 下载所有图片,保存在以name命名的文件夹中,
    '''
    path = 'F:/python_program/Paddle_Training/OneDay_basicKnowledge/'+'pics/'+name+'/'
    if not os.path.exists(path):
      os.makedirs(path)

    for i, pic_url in enumerate(pic_urls):
        try:
            pic = requests.get(pic_url, timeout=15)
            string = str(i + 1) + '.jpg'
            with open(path+string, 'wb') as f:
                f.write(pic.content)
                #print('成功下载第%s张图片: %s' % (str(i + 1), str(pic_url)))
        except Exception as e:
            #print('下载第%s张图片时失败: %s' % (str(i + 1), str(pic_url)))
            print(e)
            continue
if __name__ == '__main__':

     #爬取百度百科中《乘风破浪的姐姐》中参赛选手信息,返回html
     html = crawl_wiki_data()

     #解析html,得到选手信息,保存为json文件
     parse_wiki_data(html)

     #从每个选手的百度百科页面上爬取,并保存
     crawl_everyone_wiki_urls()

     print("所有信息爬取完成!")

  爬取的结果如下:

  每个文件夹下均是每位明星的照片

  爬取数据之后,我们对明星的年龄进行分析,当然大家也可以对其身高、体重以及其他进行分析,其实,代码都差不多的,具体实现如下:

import matplotlib.pyplot as plt
import json


with open('F:/python_program/Paddle_Training/OneDay_basicKnowledge/stars_info.json', 'r', encoding='UTF-8') as file:
         json_array = json.loads(file.read())

#绘制选手年龄分布柱状图,x轴为年龄,y轴为该年龄的小姐姐数量
birth_days = []
for star in json_array:
    if 'birth_day' in dict(star).keys():
        birth_day = star['birth_day']
        if len(birth_day) == 4:
            birth_days.append(birth_day)

birth_days.sort()
print(birth_days)

birth_days_list = []
count_list = []

for birth_day in birth_days:
    if birth_day not in birth_days_list:
        count = birth_days.count(birth_day)
        birth_days_list.append(birth_day)
        count_list.append(count)

print(birth_days_list)
print(count_list)

# 设置显示中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
plt.figure(figsize=(15,8))
plt.bar(range(len(count_list)), count_list,color='r',tick_label=birth_days_list,
            facecolor='#9999ff',edgecolor='white')

# 这里是调节横坐标的倾斜度,rotation是度数,以及设置刻度字体大小
plt.xticks(rotation=45,fontsize=20)
plt.yticks(fontsize=20)

plt.legend()
plt.title('''《乘风破浪的姐姐》参赛嘉宾''',fontsize = 24)
plt.savefig("bar_result01.png")
plt.show()

  分析结果如下:

  不过,在分析数据之前,我们在后台应该通过pip安装好matplotlib三方库,具体安装命令如下:

python -m pip install -U pip setuptools -i https://pypi.douban.com/simple

  具体结果如下

python -m pip install matplotlib -i https://pypi.douban.com/simple

  具体结果如下

  最后验证matplotlib是否安装成功

import matplotlib

  具体结果如下

  如果输入以下代码,可以出现以下结果,则说明安装成功

import matplotlib.pyplot as plt
labels='frogs','hogs','dogs','logs'
sizes=15,20,45,10
colors='yellowgreen','gold','lightskyblue','lightcoral'
explode=0,0.1,0,0
plt.pie(sizes,explode=explode,labels=labels,colors=colors,autopct='%1.1f%%',shadow=True,startangle=50)
plt.axis('equal')
plt.show()

  具体结果如下

总结

  本文首先对爬虫技术设计到的原理以及网络的一些基础进行了简单的讲解,其次,对爬虫的分类以及爬虫的实际效果作出了说明,在文章最后通过三个实际案例对爬虫的爬取过程进行了详细的实践,分别从没有参数、带参数的爬虫以及最后对《乘法破浪的姐姐》进行了爬取以及对其数据就明星的年龄做了简单的分析,当然,大家可以根据本人的代码,稍作修改,就可以对明星的身高、体重以及其他进行分析。本人的这些代码均可以运行,不过需要大家提前配置好环境以及安装好python。最后,爬虫是我们做数据分析很重要的一部分,需要我们熟练掌握应用。生命不息,奋斗不止,我们每天努力,好好学习,不断提高自己的能力,相信自己一定会学有所获。加油!!!

猜你喜欢

转载自blog.csdn.net/Oliverfly1/article/details/111767847