爬虫-数据采集与处理

数据采集与处理

Posted by Silence on April 6, 2020

数据获取

常用的库有urlib,requests,aiohttp
先分别列一下官方手册:
urllib:https://docs.python.org/zh-cn/3.7/library/urllib.html
requests:https://requests.readthedocs.io/en/latest/
aiohttp: https://docs.aiohttp.org/en/stable/

urllib

官方提供,介于现在都用Python3所以urlib和urlib2就不再讨论了。该模块下有urllib.request,urllib.error,urllib.parse,urllib.rebotparser四个包。主要使用request,因为该库实在过于反人类,不能说是符合Python之禅的精神。暂不过多介绍,详情看手册即可。

Request

该部分,第一版:2020-04-08

(先有一点点不太成熟的想法,个人觉得Python官方提供的urllib实在是过于反人类,各种方法乱的一匹,导致菜鸡如我学了几次放弃了几次,直到遇到Requests库。requests库做的很好的一点就是封装的极为简洁,简洁高效)

全文的撰写思路基于如何构造一个HTTP请求,然后完成整个请求过程。逐步介绍如何使用Requests库中提供的函数完成我们构造该请求的过程,以及如何获取响应,为我们的爬虫提取数据。
因此使用起来就像用BurpSuite抓改包一样,选个自己需要的请求方式,依次给定各个请求头,给上需要传递的数据,发送请求,收到响应,通过响应对象的属性获取所需的信息,异常简单。

请求

Request库当中所有的功能都可以通过7个方法来访问,列在下面。这些方法全部都会返回一个Response的对象。

  • requests.request(method, url,**kwargs)
  • requests.head(url,**kwargs)
  • requests.get(url,parms=None,**kwargs)
  • requests.post(url,data=None,json=None,**kwargs)
  • requests.put(url,data=None,**kwargs)
  • requests.patch(url,date=None,**kwargs)
  • requests.delete(url,**kwargs)

通过requests.request()方法来讲,其他结果方法基本上就是该方法的简化或专项化。另外该方法支持自定请求动词,满足CTF中某些题目的要求。
可传递的参数有:

method:请求方式
url:请求网址
data:数据,字典或者元组都可以。表单编码后传递或者类文件对象在请求体传递
json:传递json数据
headers:指定请求头
cookies:指定cookies
file:上传文件
auth:给定基础的HTTP认证信息
timeout:指定超时时间
allow_redirects:是否允许重定向
proxies:执行代理
stream:流式传输

下面介绍一下请求方法:

1.get方式
传递参数:参数可通过get方法的params字段传递一个字典,如
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get(url, params=payload)
定制请求头:定制自己想要的头部
>>> headers = {'user-agent': 'my-app/1.1.1'}
>>> r = requests.get(url, headers=headers)
Cookies信息:
>>> url = 'http://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)

2.post方式
构造表单数据:
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post(url, data=payload)
上传文件:
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files)

3.其他参数
超时:
r = requests.get('https://github.com', timeout=5)
还可传入元祖,用作connect和read的timeout:
r = requests.get('https://github.com', timeout=(3.05, 27))

响应

通过上面7种方法都可以获得一个response对象。我们想要获取的关于响应的属性,内容等都可以通过这个对象来获取。
常用的属性有:

import requests
r = requests.get(url)
# 响应内容,根据r.encoding编码
r.text
# 响应内容,二进制格式
r.content
# Cookies
r.cookies
# 响应时间(还没试过,看看能不能在跑SQL注入的时候用)
r.elapsed
# 编码方式,可用于重新编码后存入文件
r.encoding
# 响应头
r.headers
# 请求历史,除HEAD,requests会自动处理所有的重定向,可以根据该属性来跟踪
r.history
# json内容
r.json
# 原始响应内容,从套接字获取,需要在请求的时候设置stream=True
r.raw
# 状态码文字表达
r.reason
# 状态码
r.status_code

错误与异常

一共有8个错误。分别是:

  • requests.RequestException(*args, **kwargs)
  • requests.ConnectionError(*args, **kwargs)
  • requests.HTTPError(*args, **kwargs)
  • requests.URLRequired(*args, **kwargs)
  • requests.TooManyRedirects(*args, **kwargs)
  • requests.ConnectionTimeout(*args, **kwargs)
  • requests.ReadTimeout(*args, **kwargs)
  • requests.Timeout(*args, **kwargs)

会话

requests的一个类,requests.session。会话对象能跨请求保持某些参数,在同一个session实例发出的所有请求之间保持cookie。详细参数,函数请参考官方手册。

>>> s = requests.session()
>>> s.get(url)
>>> with requests.session as s:
        s.get(url)

代理:

HTTP代理
proxies = {
  "http": "http://10.10.1.10:3128",
  "https": "http://10.10.1.10:1080",
}
requests.get("http://example.org", proxies=proxies)

也可以通过环境变量HTTP_PROXY 和 HTTPS_PROXY 来配置代理
如果需要使用HTTP Basic Auth,可以使用:
proxies = {
    "http": "http://user:pass@10.10.1.10:3128/",
}
这样的语法来实现

Socks代理
需安装依赖,pip install requests[socks]
proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}

身份认证

简单介绍几种身份认证方式

1.基本身份认证
>>> from requests.auth import HTTPBasicAuth
>>> requests.get('https://api.github.com/user', auth=HTTPBasicAuth('user', 'pass'))
或者更为简单的可以直接作为参数传入请求函数:
>>> requests.get('https://api.github.com/user', auth=('user', 'pass'))
2.OAuth认证
>>> import requests
>>> from requests_oauthlib import OAuth1
>>> url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
>>> auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',
...               'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
>>> requests.get(url, auth=auth)

解析数据

常用的库有re,beautifulsoup4,lxml,pyquery
先分别列一下官方手册:
re:https://docs.python.org/zh-cn/3.7/library/re.html
beautifulsoup4:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/
lxml:https://lxml.de/index.html#documentation
pyquery:

re

简单记录一些元字符:

\ ^ $ * + . {n,m} ?
(pattern) x|y [xyz] [^xyz]
[a-z] [^a-z] 

几个修饰符:

re.A:让\w,\W,\b,\B,\d,\D,\s,\S只匹配ASCII
re.I:忽略大小写
re.S:使.匹配包括换行符在内的所有字符
re.M:影响^和$,对每一行都查而非只对字符串
re.X:允许以更加灵活的方式写正则表达式,更为易于理解,可加#

常用函数:

re.search(pattern,string,flags=0)
扫描整个字符串返回第一个匹配结果
re.match(pattern,string,flags=0)
从字符串起始位置进行匹配,如不是从起始位置匹配成功返回None
re.spilt(pattern,string,maxspilt=0,flags=0)
用pattern分割字符
re.findall(pattern,string,flags=0)
对string返回一个不重复的pattern的匹配列表。如果样式中存在多个组,返回一个组合列表(元组的列表)
re.finditer(pattern,string,flags=0)
同上,返回的是一个迭代器
re.sub(pattern,repl,string,count=0,flags=0)
re.escape(pattern)
如果字符串中含有任意包含正则表达式元字符,转义

匹配对象方法和属性

Match.expand(template)
对template进行反斜杠转义并返回
Match.group([group1,···])
返回一个或者多个匹配的子组,也可以用(?P<name>···)的方式来确定
Match.[n]
可相当于Match.group(n)
Match.groups(default=None)
返回一个元组,包含所有匹配的子组
Match.groupdict()
返回一个字典,包含了所有的命名子组
Match.start()
Match.end()
返回group匹配到的字符串的开始和结束标号
Match.span()
返回一个二元组(m.start(group),m.end(group))
Match.re
返回正则表达式
Match.string
传递到match()和search()的字符串

Beautiful Soup

可以从HTML或XML文档中提取数据的Python库。可选择不同的解析器:Python标准库html.parser,lxml,html5lib
给出脑图一份: BeautifulSoup4 掌握三个点:对象,节点,搜索方法。
对象:Tag,NavigaleString,BeautifulSoup,Comment
节点:各标签。在bs4的处理下,会有各自的属性,父节点,兄弟节点,字符串内容等等
搜索方法:最常用find,find_all。各自的方法又可以直接使用.xx的方式以属性调用

lxml

暂时不做讲解

缓存与持久化

常用的库有pymysql,sqlalchemy,peewee,redis,pymongo
先分别列一下官方手册:
PyMysql:https://pymysql.readthedocs.io/en/latest/index.html
sqlalchemy:https://docs.sqlalchemy.org/en/13/
peewee:http://docs.peewee-orm.com/en/latest/
pymango:https://pymongo.readthedocs.io/en/stable/

PyMysql

两个对象,带了一堆方法。
连接Connection,游标Cursor。
指定执行语句execute,执行语句commit,获取结果fetch(one,all,many) 使用官方文档中的例子就一目了然了:

import pymysql.cursors

# Connect to the database
connection = pymysql.connect(host='localhost',
                             user='user',
                             password='passwd',
                             db='db',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)

try:
    with connection.cursor() as cursor:
        # Create a new record
        sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
        cursor.execute(sql, ('webmaster@python.org', 'very-secret'))

    # connection is not autocommit by default. So you must commit to save
    # your changes.
    connection.commit()

    with connection.cursor() as cursor:
        # Read a single record
        sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s"
        cursor.execute(sql, ('webmaster@python.org',))
        result = cursor.fetchone()
        print(result)
finally:
    connection.close()

sqlalchemy

peewee

redis

pymango

生成数字签名

常用的库有hashlib