美文网首页
数据采集-爬虫基本原理

数据采集-爬虫基本原理

作者: CoderInsight | 来源:发表于2023-02-12 08:48 被阅读0次

    0.爬虫基本原理

    爬虫是 模拟用户在浏览器或者App应用上的操作,把操作的过程、实现自动化的程序

    当我们在浏览器中输入一个url后回车,后台会发生什么?比如说你输入https://www.baidu.com

    简单来说这段过程发生了以下四个步骤:

    • 查找域名对应的IP地址。
      浏览器首先访问的是DNS(Domain Name System,域名系统),dns的主要工作就是把域名转换成相应的IP地址
    • 向IP对应的服务器发送请求。
    • 服务器响应请求,发回网页内容。
    • 浏览器显示网页内容。
    01-webdns.jpg

    网络爬虫要做的,简单来说,就是实现浏览器的功能。通过指定url,直接返回给用户所需要的数据, 而不需要一步步人工去操纵浏览器获取。

    (1).浏览器是如何发送和接收这个数据呢?

    HTTP协议(HyperText Transfer Protocol,超文本传输协议)目的是为了提供一种发布和接收HTML(HyperText Markup Language)页面的方法。

    HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。

    (2).爬虫基本思路

    (1).通过URL或者文件获取网页,
    (2).分析要爬取的目标内容所在的位置
    (3).用元素选择器(re、BeautifulSoup、Xpath等)快速提取(Raw)目标内容
    (4).处理提取出来的目标内容 (通常整理合成一个 Json)
    (5).存储处理好的目标内容(比如放到 MongoDB、MySQL 之类的数据库,或者写进文件里.)
    

    1.静态本地网页与json文件爬取演示

    (1).html网页抓取

    import urllib.request as re
    from bs4 import BeautifulSoup
    # 1.使用urllib的request模块,打开一个带有html页面的链接,此时获取的只是一个链接对象
    response = re.urlopen("file:///D:/Project/Python/jyputerTest/Spider/Source/demo01.html")
    # 2.从连接对象中将网页源代码字符串获取到,此时是没有经过格式化的
    html = response.read()
    # 3.通过 BeautifulSoup 对网页源代码字符串进行解析
    soup = BeautifulSoup(html)
    # 4.我们就可以从BeautifulSoup中直接使用标签名作为参数 去直接获取对应 标签 中的数据
    soup.find('tr')
    soup.find_all('tr')
    

    (2).json数据读取

    import urllib.request as re
    # 此时我们解析json使用的模版是json
    import json
    
    # 1.同样此时获取我们文件的连接
    response = re.urlopen("file:///D:/Project/Python/jyputerTest/Spider/Source/demo02.json")
    # 2.将连接中的数据读取为源字符串
    jsonString = response.read()
    # 3.加载数据为json,同时对读取到的json字符串做一个解码
    jsonObject = json.loads(jsonString.decode())
    # 4.对json中的数据进行值的获取(分不同的层级)
    jsonObject['employees']  # 根路径
    jsonObject['employees'][0] # 获取列表中的第一个元素
    jsonObject['employees'][0]['firstName'] # 获取字典中指定的key对应的值(value)
    

    2.爬虫实战:7日天气预报 爬取

    本次实战主要是使用 request库 和 BeautifulSoup 库进行数据的爬取

    (1).实战原理

    1),Requests 简介

    request库是一个非常好用的请求发送包,它可以自动封装底层python 的网络请求。


    request常用方法及说明.jpg

    其中我们本次课程中是使用的:Requests.get()方法
    Requests库的get()方法主要用于获取HTML网页,相当于HTTP的GET。其返回对象response的常用属性如下表所示,我们可通过这些属性获取要访问域名的基本信息。

    2),BeautifulSoup 简介

    BeautifulSoup库是用Python语言编写一个HTML/XML的解释器。它可以很好地处理不规范的标记并生成剖析树,其本身提供了简单又常用的导航、搜索以及修改剖析树的操作,利用它可以大大缩减编程时间。

    简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据。

    Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。

    BeautifulSoup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐使用lxml 解析器。


    BeautifulSoup支持的解析器.png

    3),网页信息查找与提取

    我们使用Chrome浏览器,对于要爬取的网页,可以按F12进入调试模式,也可以选择相应信息右键"检查",亦可进入调试模式,而且直接到达该信息HTML位置,更为方便。

    (2).实战代码

    # 导入requests模块和bs4模块
    import requests
    from bs4 import BeautifulSoup
    
    # 加入请求头,防止网站监测出来我们是爬虫,所以都必须要引入请求;对于有需要 登录 的页面需要加上cookices,那么直接在header字典中将 " 'Cookie': '你的cookie' "添加进去
    header={
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
    }
    # 引入要爬取的网页的url
    url = 'http://www.weather.com.cn/weather/101120101.shtml'
    
    # 1、获取网页的响应
    # url,请求头,设置
    response = requests.get(url, headers=header,timeout=30)
    # 2,当网页获取成功的之后,设置网页的编码格式,方式出现中文的乱码
    response.encoding='utf-8'
    
    # 3,从获取到的文本连接中获取对应的网页源代码字符串
    data= response.text
    # print(text)
    
    # 4,通过 BeautifulSoup 对网页源代码字符串进行解析;第二个参数是:指定 BeautifulSoup 使用什么格式进行解析(这里使用的是lxml :在你安装BeautifulSoup的时候默认是没有这个库的,需要你手动去安装 pip install lxml)
    soup = BeautifulSoup(data,'lxml')
    # print(soup)
    
    # find()的作用是只去找 指定样式中 第一个出现的
    ul = soup.find(class_='t clearfix')
    # print(ul)
    
    ul_li = ul.find_all('li')
    # 遍历打印所有的元素
    for item in ul_li:
        # print(item)
        # 根据标签获取值
        # 1.获取当前日期
        day = item.find('h1').text
        # 2.获取当前日期下对应的天气
        wea = item.find(class_='wea').text
        # 3.获取当前日期下对应温度区间
        tem = item.find(class_='tem').text
        # 如果想获取最低气温(可以直接使用下标的方式直接去处理温度区间中的值)
        # print(tem[4:])
        # 4.取风向,使用find函数默认是在item中取第一次出现的em标签;对应的可以直接再去取span标签的值,就直接用“.标签案名”的方式进行获取
        ws = item.find('em').span.attrs['title']
        # 5.把两个风向都取出来的话,可以使用findall,此时先把所有的带有风向的span标签获取出来
        ws_all_span = item.find('em').find_all('span')
        # 使用 推导式 的方式将所有风向获取出来,并返回一个列表
        ws_all = [ws['title'] for ws in ws_all_span]
        # 6.获取风力,先拿到class,再拿到对应的i,对应的文本,然后将 "<" 做一个替换 "小于"
        win = item.find(class_='win').i.text.replace("<","小于")
        
        print('-----------------------------begin---------------------------------------------------------------------')
        print('-----------------------------1:日期-----------------------------------------------------------------')
        print(day)
        print('-----------------------------2:天气-----------------------------------------------------------------')
        print(wea)
        print('-----------------------------3:温度-----------------------------------------------------------------')
        print(tem)
        print('-----------------------------4:只获取第一个风向-----------------------------------------------------------')
        print(ws)
        print('-----------------------------5:两个风向都取出来-----------------------------------------------------')
        # 在输出的时候使用sep指定分隔符
        print(ws_all[0],ws_all[1],sep=',')
        print('-----------------------------6:风级-----------------------------------------------------------------')
        print(win)
        print('-----------------------------end---------------------------------------------------------------------')
    

    (3).find() 参数总结

    每一次执行find() 之后的返回值,仍然是一个对象(bs4.element.Tag),还可以继续调用对象中的 "." 方法。

    参数 对应含义
    find("XXX") 传入默认参数表示:查询指定 "XXX" 标签第一次出现的位置对应的属性值
    find_all("XXX") 传入默认参数表示:查询指定 "XXX" 标签所有出现的位置对应的属性值
    find(class_='XXX') 传入 "class_ " 参数表示:查询指定CSS属性为 "XXX" 的值
    find(id="XXX") c传入"id" 参数表示:查找指定id属性为 "XXX" 的值
    find("XXX").span 配合 "." 参数的时候,来获取 "XXX" 标签下嵌套的 "span" 标签(在之后的代码中可以对Span标签中的值进行进一步的值的获取)
    find("XXX").span.attrs['title'] 配合 "attrs["xxx"]" 参数的使用,来获取嵌套在"XXX" 标签下对应 "span "标签中指定的属性值,例如可以获取 "span" 标签中的 "title" 属性的值;同理:如果此时的 "span" 标签 换成 "a" 标签,那么此时再使用 ".attrs["href"]" 就可以获取 "a" 标签中的 "href" 属性的值,从而进行连接的跳转。
    find("XXX").span['title'] 和上一个表格中的实现效果是等价的

    (4).select() 选择器参数总结

    在CSS中,标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list。

    以下的 select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 get_text() 方法来获取它的内容;获取将其中的值取出来,此时其类型仍然是一个对象(bs4.element.Tag)

    参数 对应含义
    soup.select('xxx') 通过标签名进行查找
    soup.select('.xxx') 通过类名进行查找,也就是class属性
    soup.select('#xxx') 根据id名查找
    soup.select('p #xxx') 组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的。例如查找 p 标签中,id 等于 xxx 的内容,二者需要用空格分开
    soup.select("head > title") 直接子标签查询,也是查询指定标签下的某一个标签。例如查询 p 标签下的 title 标签
    soup.select('a[class="sister"]') 根据属性查找:查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。

    3.re(正则表达式)的使用与图片文件的下载

    此时以 "2.爬虫实战:7日天气预报 爬取" 的结果为例进行数据的保存:
    此时已经有了从网页中获取的html文本

    • 爬虫代码
    # 导入requests模块和bs4模块
    import requests
    from bs4 import BeautifulSoup
    
    # 加入请求头,防止网站监测出来我们是爬虫,所以都必须要引入请求;对于有需要 登录 的页面需要加上cookices,那么直接在header字典中将 " 'Cookie': '你的cookie' "添加进去
    header={
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
    }
    # 引入要爬取的网页的url
    url = 'http://www.weather.com.cn/weather/101120101.shtml'
    
    # 1、获取网页的响应
    # url,请求头,设置访问超时
    response = requests.get(url, headers=header,timeout=30)
    # 2,当网页获取成功的之后,设置网页的编码格式,方式出现中文的乱码
    response.encoding='utf-8'
    
    # 3,从获取到的文本连接中获取对应的网页源代码字符串
    data= response.text
    print(data)
    
    • 图片文件的保存
    # 使用正则进行匹配
    import re
    # 此时的正则表达式的含义是:以 src=开头,然后 .png 结尾,中间是啥都行,而且此时是懒惰匹配
    images = re.findall(r'src="(.*?\.png)"',data)
    
    # 1.单张图片的保存
    response = requests.get(images[0])
    img = response.content
    title=0
    path = r'./picture/%s.jpg'%(title)
    with open(path,'wb') as f:
        f.write(img)
        
    # 2.批量url连接图片的保存 
    # 每个文件的命令方式是从 0 开始递增的
    count = 0
    for url in images:
        response = requests.get(url)
        img = response.content
        count = count + 1
        # 使用C语言的格式(类似与占位符的方式)进行拼接 路径字符串 (此时的路径需要提前创建!!!或者再引入os库将创建文件夹)
        path = r'./picture/%s.jpg'%(count)
        # 以写和二进制的权限,打开文件,此时的文件会自动创建
        with open(path,'wb') as f:
            # 使用写的方式将文件下载到指定的路径汇总
            f.write(img)
    

    (1),正则表达式

    1).常用元字符:

    代码 说明
    . 匹配除换行符以外的任意字符
    \w 匹配字母或数字或下划线或汉字
    \s 匹配任意的空白符
    \d 匹配数字
    \b 匹配单词的开始或结束
    ^ 匹配字符串的开始
    $ 匹配字符串的结束

    2),字符转义

    如果想查找元字符本身的话,得使用\来取消其特殊含义。比如.可以使用 \.的方式进行转义。

    3).重复次数:常用限定符

    代码/语法 说明
    * 重复零次或更多次
    + 重复一次或更多次
    ? 重复零次或一次
    {n} 重复n次
    {n,} 重复n次或更多次
    {n,m} 重复n到m次

    4),字符类

    对于需要匹配多个字符的时候,我们需要通过列表的方式进行指定,可以指定多个不同的字符,例如(a,e,i,o,u);或者是指定一个区间,例如:([0-9])

    5),反义代码

    代码/语法 说明
    \W 匹配任意不是字母,数字,下划线,汉字的字符
    \S 匹配任意不是空白符的字符
    \D 匹配任意非数字的字符
    \B 匹配不是单词开头或结束的位置
    [^x] 匹配除了x以外的任意字符
    [^aeiou] 匹配除了aeiou这几个字母以外的任意字符

    6),贪婪与懒惰

    • 当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪*匹配。

    • 有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:

      a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。

    代码/语法 说明
    *? 重复任意次,但尽可能少重复
    +? 重复1次或更多次,但尽可能少重复
    ?? 重复0次或1次,但尽可能少重复
    {n,m}? 重复n到m次,但尽可能少重复
    {n,}? 重复n次以上,但尽可能少重复

    4.爬虫实战:豆瓣电影信息 爬取

    本次实战主要是使用 request库与 Xpath库进行操作

    (1).实战原理

    1),Xpath 简介

    Xpath 即为 XML 路径语言(XML Path Language),它是一种用来确定 XML 文档中某部分位置的语言。Xpath 基于 XML 的树状结构,提供在数据结构树中找寻节点的能力。起初 Xpath 的提出的初衷是将其作为一个通用的、介于 Xpointer 与 XSL 间的语法模型。但是Xpath 很快的被开发者采用来当作小型查询语言。

    Xpath解析网页的流程:


    Xpath流程简介.jpg

    1.首先通过Requests库获取网页数据;

    2.通过网页解析,得到想要的数据或者新的链接;

    3.网页解析可以通过 Xpath 解析网页,然后通过定位元素,然后再右键使用Copy的方式获取元素的Xpath信息并获得文本:

    file=s.xpath('元素的Xpath信息/text()')

    (2).实战代码

    # 导入requests模块和bs4模块
    import requests
    from lxml import etree
    
    # 引入要爬取的网页的url
    url = 'https://movie.douban.com/subject/1292052/'
    # 加入请求头,防止网站监测出来我们是爬虫,所以都必须要引入请求;对于有需要 登录 的页面需要加上cookices,那么直接在header字典中将 " 'Cookie': '你的cookie' "添加进去
    header={
       "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36"
    }
    # 1、获取网页的响应
    # url,请求头,设置访问超时时间
    response = requests.get(url, headers=header,timeout=30)
    # 2,当网页获取成功的之后,设置网页的编码格式,方式出现中文的乱码
    response.encoding='utf-8'
    
    # 3,从获取到的文本连接中获取对应的网页源代码字符串
    data = response.text
    
    # 4.对获取到的网页源代码字符串使用etree解析
    s=etree.HTML(data)
    
    # 5.通过从网页中Copy的Xpath路径,依次获取电影名称、导演、主演、时长信息
    film=s.xpath('//*[@id="content"]/h1/span[1]/text()')
    director=s.xpath('//*[@id="info"]/span[1]/span[2]/a/text()')
    actor=s.xpath('//*[@id="info"]/span[3]/span[2]/a/text()')
    time=s.xpath('//*[@id="info"]/span[13]/text()')
    
    # 6.显示获取的信息内容
    print('name:',film)
    print('director:',director)
    print('actors:',actor)
    print('duration:',time)
    

    (3).Xpath代码的来源

    获取电影名称
    元素的Xpath信息是需要我们手动获取的,获取方式为:定位目标元素(电影名称:肖申克的救赎),在网站上选取的目标元素上依次点击:右键 > 检查,按住快捷键“shift+ctrl+c”,移动鼠标到对应的元素时即可看到对应网页代码:

    在网页中查看指定标签的Xpath_01.png

    在电影标题对应的代码上依次点击 右键 > Copy > Copy XPath,获取电影名称的Xpath:


    在网页中查看指定标签的Xpath_02.png

    此时得到的标签示例 "//*[@id="content"]/h1/span[1]"

    # 1,直接将获取到Xpath打印出来,得到的是一个元素
    print(s.xpath('//*[@id="content"]/h1/span[1]'))
    # 输出结果:[<Element span at 0x1a4e1c0afc8>]
    
    # 2,可以再追加"/text()",获取元素中对应的值
    print(s.xpath('//*[@id="content"]/h1/span[1]/text()'))
    # 输出结果:['肖申克的救赎 The Shawshank Redemption']
    

    同理可以得到其他元素的标签!!!

    5.文本文件的保存

    (1).保存字符串到本地txt文件中

    此时以 "4.爬虫实战:豆瓣电影信息 爬取" 的结果为例进行数据的保存:
    此时已经有了 “film”,“director”,“actor”,“time”变量对应的属性值

    • 爬虫代码
    # 导入requests模块和bs4模块
    import requests
    from lxml import etree
    
    # 引入要爬取的网页的url
    url = 'https://movie.douban.com/subject/1292052/'
    # 加入请求头,防止网站监测出来我们是爬虫,所以都必须要引入请求;对于有需要 登录 的页面需要加上cookices,那么直接在header字典中将 " 'Cookie': '你的cookie' "添加进去
    header={
       "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36"
    }
    # 1、获取网页的响应
    # url,请求头,请求延迟(时间):主要是在多批次访问的时候,有一个停顿时间,可以防止网站监测为爬虫,也就是伪造一个真实的人的请求
    response = requests.get(url, headers=header,timeout=30)
    # 2,当网页获取成功的之后,设置网页的编码格式,方式出现中文的乱码
    response.encoding='utf-8'
    
    # 3,从获取到的文本连接中获取对应的网页源代码字符串
    data = response.text
    
    # 4.对获取到的网页源代码字符串使用etree解析
    s=etree.HTML(data)
    
    # 5.通过从网页中Copy的Xpath路径,依次获取电影名称、导演、主演、时长信息
    film=s.xpath('//*[@id="content"]/h1/span[1]/text()')
    director=s.xpath('//*[@id="info"]/span[1]/span[2]/a/text()') actor=s.xpath('//*[@id="info"]/span[3]/span[2]/a/text()')
    time=s.xpath('//*[@id="info"]/span[13]/text()')
    
    • 文件保存代码
    # 对最后文件的输出内容进行重新格式化拼接为一个字符串,然后进行保存
    # 1,首先测试输出效果
    print("1:name: {},\n2:director: {},\n3:actor: {},\n4:time: {}".format(film[0],director[0],actor,time[0]))
    # 2,赋于变量进行保存,然后测试输出
    outData = "1:name: {},\n2:director: {},\n3:actor: {},\n4:time: {}".format(film[0],director[0],actor,time[0])
    print(outData)
    # 3,连接本地文件系统,进行文件夹与文件的创建
    # 4,首先判断要输出的文件是否存在,如果存在则直接进入到指定文件夹中,不存在则创建文件夹
    import os
    if os.path.exists("./output"):
        os.chdir("./output")
    else:
        os.mkdir("./output")
        os.chdir("./output")
    # 5,打开一个文件(若文件不存在则创建一个),然后向文件中写入数据,最后关闭与本地文件系统的连接
    # 此时就是提前创建一个带有指定路径和文件的对象,然后再调用写操作,最后将写操作关闭
    myfile = open('./out.txt','w',encoding='utf-8')
    myfile.write(outData)
    myfile.close()
    
    # 6,如果想追加数据,直接打开文件,然后通过第二个参数指定模式为追加
    # 注意此时是在创建文件对象的同时,通过文件对象带调用写操作,
    with open('./out.txt','a',encoding='utf-8') as file:
        file.write('\n123465')
    

    (2).保存字符串到Mysql数据库中

    # 1,首先在Mysql中建立你要导出的数据库和根据字段的内容创建数
    '''
    create databases test;
    use test;
    create table spiderTest(film varchar(100),director varchar(100),actor varchar(100),time varchar(100)) CHARSET=utf8;
    '''
    # 2,然后获取数据库的连接
    # 3,使用 execute()方法,执行插入语句将内容写入到数据库中
    
    import pymysql   # 导入相应操作Mysql的库文件
     
    conn = pymysql.connect(host='localhost',user='root',password='111111',db='test',port=3306,charset='utf8')
    cursor=conn.cursor()    # 连接数据库及光标
    
    # 注意此时对于演员的数据过长,所以只是截取了第一个数据写入的
    cursor.execute("insert into spiderTest(film,director,actor,time) values(%s,%s,%s,%s)",(str(film[0]),str(director[0]),str(actor[0]),str(time[0])))
    # 当执行完插入语句后需要手动提交,才会生效。
    conn.commit()
    

    相关文章

      网友评论

          本文标题:数据采集-爬虫基本原理

          本文链接:https://www.haomeiwen.com/subject/ihpfkdtx.html