Python网络爬虫笔记

基本流程

        爬虫基本流程还是很清晰的,首先是GET页面,然后对页面进行处理,提取所需信息。重点大多在GET页面和页面处理中。对于GET页面而言,其本身不应该存在技术难题,但是过于频繁的爬取REQUEST会极大的占用页面PV,影响网站用户体验。因此各大网站都会采取一定的反爬虫措施。所以这一部分的难点就是在于,如何避开反爬虫检测。假设我们爬取下来了页面,接下来要进行的就是如何对页面进行处理。一个页面多大几千条的html代码量,使得我们大部分情况下不可能用手工方式提取信息。因此我们需要对html代码文本进行分析

GET页面

几种常见的库:

import requests
import aiohttp

        大体来看,页面有两种获取方式,一种是GET方式,一种是POST方式,GET方式多返回标准HTML页面,POST凡是则多返回JSON格式数据。其中requests库整合了python中http请求的多种底层组件,相对来说比较好用,而aiohttp则是python中实现异步爬取的库,或者说python的多线程爬取,毕竟python中多线程也是依靠异步方式实现。值得注意的是,aiohttp的默认方式是一次性发送所有请求,等请求发送完毕后,再逐个对返回结果进行处理,因此当请求量比较大时,需要对数据进行分片操作。

        一个标准请求大体有两个关键信息,一个是请求的headers,这个信息告诉了服务器请求源机器的部分信息,例如操作系统,浏览器的版本,可处理的语言编码,接受的数据格式,以及表明机器身份、储存用户登陆信息的cookie等等,例子如下:

header = {
    "accept": "application/json, text/javascript",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
    "content-type": "application/x-www-form-urlencoded",
    "cookie": "tt_webid=6672604237333153294; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6672604237333153294; UM_distinctid=169b8ffba327cf-072389de003c69-9333061-1fa400-169b8ffba3335c; csrftoken=563a5c4852134b1d10e2959ac3f8512c; CNZZDATA1259612802=1127217988-1553585285-https%253A%252F%252Fwww.google.com%252F%7C1553682487; s_v_web_id=167c46be309f1736398a8f4439439b69; __tasessionId=p62q61bgc1553687603544",
    "referer": "https://www.toutiao.com/search/",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
    "x-requested-with": "XMLHttpRequest"
}

        另一个关键信息是向服务器上传的数据,在GET方式中以param传递,在POST方式中以data传递,二者和headers不同,不包含固定的字段,为网站服务自定义的信息。

param = {
    "aid": "24",
    "app_name": "web_search",
    "offset": "0",
    "format": "json",
    "keyword": "一品红",
    "autoload": "true",
    "count": "20",
    "en_qc": '1',
    "cur_tab": '1',
    "from": "search_tab",
    "pd": "synthesis",
    "timestamp": "1553687971703"
}

反爬虫机制:

        网站反爬虫的原因主要有二,一是爬虫的高请求严重影响了正常用户的体验,二是批量的数据爬取造成了企业信息的泄露。因为爬虫爬取的信息是展示在页面上的公开或半公开信息,因此网站并非是让人无法获取信息,而是不让人轻易获取这些信息。在这个逻辑下,反爬虫反的就是那些浏览行为不像正常人行为的爬虫。低端爬虫大体上有如下特点:

  • 请求频率过快,或者请求间隔固定(正常人谁会在1秒内浏览10个网页,或者固定20ms浏览一个网页)
  • 请求过多(没有人会去看所有的淘宝商品)

        针对如上思路,避免自己的爬虫被反爬,就是要伪造成正常的用户行为。例如,通过建立账号池,分散请求,使得单个账户的请求数量合理。降低请求频率,随机生成请求的间隔时间等等。当然,现代反爬虫技术已经越变越多,包括生成验证码,需要登录等等,但是其本质思路还是没有变化。这些问题的解决方法这里不展开讲了,有兴趣可以关注我的公众号,里面会不定期进行一些分享。

HTML文本分析

分析的技术:

        之前讲到爬虫返回的数据分页面和JSON数据,在python中,JSON格式文本数据可以直接被转换成dict数据进行操作,因此这里不进行讨论,只讨论返回HTML页面的情况。

        HTML是一种结构化的文本格式,因此去分析这个文本,并从中提取数据也就有了两大思路方向。一是基于文本pattern的信息提取,另一种则是基于HTML树状结构的梳理。先来说第一种,pattern提取。先来看一个html的例子:


<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <link rel="canonical" href="https://blog.csdn.net/nightwish2018"/>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="renderer" content="webkit"/>
    <meta name="force-rendering" content="webkit"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="referrer" content="always">
    <meta http-equiv="Cache-Control" content="no-siteapp" /><link rel="alternate" media="handheld" href="#" />
    <meta name="shenma-site-verification" content="5a59773ab8077d4a62bf469ab966a63b_1497598848">
        <meta name="csdn-baidu-search"  content='{"autorun":true,"install":true,"keyword":"【Felix的博客】"}'>
    
    <link href="https://csdnimg.cn/public/favicon.ico" rel="SHORTCUT ICON">
    <title>【Felix的博客】 - CSDN博客title>

        
                    <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/list-37775d3cab.min.css">
            
        
          <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/themes/skin3-template/skin3-template-9b39979775.min.css">
        <script type="text/javascript">
        var username = "nightwish2018";
        var userPermission = false;

        文本处理的思路就是去寻找数据所在位置的关键信息,例如我们要提取用户名称:nightwish2018这个信息,则可以发现在页面中,用户名前会有var username = "这一关键信息。因此我们可以通过写regex,寻找所有var username = "后的信息。这种信息效率很高,只需要遍历一遍文本就可以提取所需信息,但是假设页面中有多个关键字,则可能提取到很多无关信息,而且当pattern更加复杂时,代码的可读性降低严重,举一个血淋淋的例子,在代码review的时候,就已经完全不知道这个过程中到底提取了些什么信息:

pattern = re.compile(r"var config .*")
config = pattern.findall(raw_html)
pattern = re.compile(r"\"id\":.*?\"name\":.*?pnid")
config = pattern.findall(config[0])
pattern = re.compile(r"var option .*")
option = pattern.findall(raw_html)
pattern = re.compile(r"\"name\":.*?,\"valueitems\":")
option = pattern.findall(option[0])
pattern = re.compile(r"{\"id\".*?\"valueitems\":")
raw_index = pattern.findall(raw_html)

        因为文本处理的困难,因此提出了通过结构进行分层次查找的方式。这种方式的思路是首先将文本页面解析为树状结构的数据,然后通过寻找节点,找到对应的信息。例如在之前的页面中,如果我们想解析https://blog.csdn.net/nightwish2018这个url信息,就会发现它实际储存于head节点下的link节点中,因此我们只需要进行一次树查找,访问两个节点就可以获取信息。使用etree解析,示例如下:
原始页面:

<div class="plbox">
	<div class="plbox_img the_hover">
		<a href="/search/p_008690-c_0-b_110-t_59-a_0-d_0-p_1-l_2-pg_2.html" target="_blank">
		a>
	div>
	<h3 class="plbox_name">
		<a href="/search/p_008690-c_0-b_110-t_59-a_0-d_0-p_1-l_2-pg_2.html" target="_blank">需要的数据 
		a>
	h3>
div>

解析:

from lxml import etree
etree_html = etree.HTML(raw_html)
text = etree_html.xpath("//div[@class='plbox']/h3/a/text()")

        可以清晰看到,text中储存了在页面有class参数为plbox的div模块中,h3标题下的a模块中的需要的数据,代码更有层次感,可读性强

其他问题

  • 关于登录
            其实之前写到cookies中包含了用户登录信息,因此可以通过伪造cookie进行假登录,更多细节会在后续文章讨论。
  • 分布式爬取
            主要是为了两个目的,一是充分利用时间,既在等待页面返回的同时,处理上一个返回的页面,同时并行爬取,提高同一时间收集的信息量。二是分散请求,绕过反爬虫机制。不过对于分散请求这一目的来说,通过动态IP,伪造header等方式也可以实现,而且性价比更高。

你可能感兴趣的:(Python网络爬虫笔记)