爬虫基础和相关知识

robots协议   www.taobao.com/robots.text
1.下载数据 -urllib/requests/aiohttp
2.解析数据 -re/lxml/beautifulsoup4(bs4)/pyquery
3.持久化 - pymysql/redis/sqlalchemy/peewee/pymongo
4.调度器 - 进程/线程/协程



正则表达式 - 性能好 复杂
pyquery BeattifulSoup lxml
p>h2直接的下级
p~h2同级元素
p+h2挨着的同级元素
find('a[test=a').href查找带有test=a的a标签的内容

注意事项:
1.处理相对链接。有的时候我们从页面中获取的链接不是一个完整的绝对链接而是一个相对链接,这种情况下需要将其与URL前缀进行拼接(urllib.parse中的urljoin函数可以完成此项操作)。

设置代理服务。有些网站会限制访问的区域(例如美国的Netflix屏蔽了很多国家的访问),有些爬虫需要隐藏自己的身份,在这种情况下可以设置代理服务器(urllib.request中的ProxyHandler就是用来进行此项操作)。

限制下载速度。如果我们的爬虫获取网页的速度过快,可能就会面临被封禁或者产生“损害动产”的风险(这个可能会导致吃官司且败诉哦),可以在两次下载之间添加延时从而对爬虫进行限速。

避免爬虫陷阱。有些网站会动态生成页面内容,这会导致产生无限多的页面(例如在线万年历等)。可以通过记录到达当前页面经过了多少个链接(链接深度)来解决该问题,当达到事先设定的最大深度时爬虫就不再像队列中添加该网页中的链接了。

SSL相关问题。在使用urlopen打开一个HTTPS链接时会验证一次SSL证书,如果不做出处理会产生错误提示“SSL: CERTIFICATE_VERIFY_FAILED”,可以通过以下两种方式加以解决:

使用未经验证的上下文

import ssl

request = urllib.request.Request(url='...', headers={...}) 
context = ssl._create_unverified_context()
web_page = urllib.request.urlopen(request, context=context)
设置全局的取消证书验证

import ssl

ssl._create_default_https_context = ssl._create_unverified_context
涉及到重要数据都会使用关系型数据库,因为关系型数据库提供了锁机制

@@tx_isolation 数据库事务性

数据库中通过select @@tx_isolation查看
事务隔离级别:
- uncommited read -Dirty Read 读未提交
- read commited 读提交 会产生不可重复读
- repeatable read 可重复读 产生幻读
- serializable 单条执行,高的执行级别,但执行效率低
事务,会自动生成锁
更改隔离级别:

 set global transaction islocation level read commited(更改全局事务隔离级别为读提交)
  set session transaction islocation level serializable(临时更改局部隔离级别为最高级别)

保存多种属性,比如二手商品接口不能创建表格来保存数据,可以使用灵活的非关系数据库

压缩数据:
1.压缩数据需要先对数据执行序列化:
    序列化 -- 把对象变成字符或者字节序列
    pickle / json / shelve
    json(将数据序列化成字符):dump /dumps /load /loads
    反序列化  -- 把自负或者字节对象还原成对象
2.数据压缩
    zlib模块 :Compress.compress(data)/压缩,Compress.decompress(data)/解压缩

压缩后如果要存放在mysql里面,需要将字段模型变为longblob(长的二进制数据),放大的文本内容用longtext

request官方文档http://docs.python-requests.org/zh_CN/latest/user/quickstart.html

有时候我们爬取网页时会出现400 bad connetion,这是由于对方网页限制了了我们的爬取行为,这时候我们需要对自己的ip地址进行伪装,主要方式有更改headers和代理

1.更改headers:
headers = {'user-agent':'Baiduspider'}

将自己伪装成百度的爬虫,那么在提交get请求时要将自己伪造的headers提交上去

resp=requests.get(seed_url,headers=headers)

2.代理:主要是采用哪些不能绑定身份识别的服务器,如购买海外服务器或者国内的西刺代理。

proxies ={
        'http':'http://183.128.243.71:6666'
    }

提交请求的时候

 resp = requests.get(seed_url,headers=headers,proxies=proxies)

哈希摘要:
保存url或者密码时都是通过对上传的数据通过哈希算法进行计算,结果上传到数据库,生成的哈希摘要是惟一的,不可逆的,安全。通常有md5,sha1,sha256,sha512四中算法

扫描二维码关注公众号,回复: 1466313 查看本文章
redis数据持久化:
1.AOF:

原理是将Reids的操作日志以追加的方式写入文件,还原操作即可还原数据,数据放在appendonly.aof文件中。

1). 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。

2). 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。

3). 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

4). AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

AOF的劣势有哪些呢?

1). 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

2). 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

2.RDB:

将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化,
1). 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。

2). 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。

3). 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

4). 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

RDB又存在哪些劣势呢?

1). 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

2). 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

摘要

如果要数据持久化,需要在这步操作之前生成摘要,用python封装的hasher方法

def main():
    # 指定种子页面
    base_url = 'https://www.zhihu.com/'
    seed_url = urljoin(base_url, 'explore')
    # 创建Redis客户端
    client = Redis(host='1.2.3.4', port=6379, password='1qaz2wsx')
    # 设置用户代理(否则访问会被拒绝)
    headers = {'user-agent': 'Baiduspider'}
    # 通过requests模块发送GET请求并指定用户代理
    resp = requests.get(seed_url, headers=headers,verify=False)
    # 创建BeautifulSoup对象并指定使用lxml作为解析器,verify表示是否验证证书
    soup = BeautifulSoup(resp.text, 'lxml')
    href_regex = re.compile(r'^/question')
    # 将URL处理成SHA1摘要(长度固定更简短)
    hasher_proto = sha1()
    # 查找所有href属性以/question打头的a标签
    for a_tag in soup.find_all('a', {'href': href_regex}):
        # 获取a标签的href属性值并组装完整的URL
        href = a_tag.attrs['href']
        full_url = urljoin(base_url, href)
        # 传入URL生成SHA1摘要
        hasher = hasher_proto.copy()
        hasher.update(full_url.encode('utf-8'))
        field_key = hasher.hexdigest()
        # 如果Redis的键'zhihu'对应的hash数据类型中没有URL的摘要就访问页面并缓存
        if not client.hexists('zhihu', field_key):
            html_page = requests.get(full_url, headers=headers).text
            # 对页面进行序列化和压缩操作
            zipped_page = zlib.compress(pickle.dumps(html_page))
            # 使用hash数据类型保存URL摘要及其对应的页面代码
            client.hset('zhihu', field_key, zipped_page)
    # 显示总共缓存了多少个页面
    print('Total %d question pages found.' % client.hlen('zhihu'))
    client.expire('zhuhu',86400) # 设置超期时间,redis中使用```ttl zhihu```查看过期时间 


if __name__ == '__main__':
    main()

运行上面的代码将数据缓存到redis中,我们在redies的客户端中执行hkeys zhihu可以看到

 1) "6a793b0f0e16143b8ef3c4be65855305c1284085"
 2) "0cb37c9547fe00443137b2669cba50648f7e362f"
 3) "0d844df25ab64194fc095574725e905d1e448dd6"
 4) "34395846df4f6ccd5c0044677fdb7accd507ce8d"
 5) "1f67037b2fd0bb93f2d94eb17dea8f651cc057fd"
 6) "6ac8a1b5898fc08dcaf2f9b472fb9457c2318a02"
 7) "0b824321002368c314736d9b4bd0f0ff399fe09f"
 8) "32ab432f4a4d4a525e32c8e28e3eeccabc244474"
 9) "14c9f3caf334ed1a24d7f8861c3a801c20bab767"
10) "7485df57b165ee204d7f41dae0cb0bdfd9f79d4f"
11) "467a60f28eb7fa3fed922a09ed1b834283a615fd"
12) "f8e2eae79ab9673559a36885fdcb46a38337ab43"
13) "f571b9935d225be267f687f8bfdd01e02aa151a2"

上面的数据是我们爬取的文件通过压缩后再生成的摘要,我们通过112.74.60.22:1994> hget zhihu f571b9935d225be267f687f8bfdd01e02aa151a2选取一组数据查看:

``
"x\x9c\xed}ks\x13I\xb2\xe8\x89\xb8\xdf\xee\xaf\xd0\xf1\x8d\xd8/w$\xf7\xfb\xc1\x02\x11\xc0\xcc\xec\xcc9\xc0\xb0\x03\xb3{\xee\x9e\xb8\xa1h\xb5ZV\xefHjm\xb7d\x03\x1bD\x98\xa7\r\x18\xdb\xcc0<=<fx\xcd\x0c
\xd8\x01\xe3\a\xe0\x88\xf3+\xee\xe7]\xb5$\x7f\xda\xbfp3\xab\xba[\xdd\xad\x96,c8\xcb\xee\x12\x01r\xab\xab*++3+3\xab2\xab4\xfe?\xfec\xf7\xff\xfb\x97\x7f\xd9\xfa\xafyK\xaf\x1d\xaa\x1a\xa9b\xad\\xda\xfe?\xb7\xe2\x9fTI\xab\x8cl\x1b:\\x1cJ\xe5\xb5\x9a\x96.j\xa6]2+\xc6\xb6\xa1\x9a]7\xbc\x97\xb5\xa2Q\x867%s\xa4X\x1b\xda\xbe\xb5hh\xf9\xed[\xcbFMK\xe9E\xcd\xdeo\xd4\xb6\r\xd5k\x85\xb424\xbc}k\xcd\xac\x95\x0c\xda\xcc64\xbd\x96.\x1a\xa52\xd6 \xf0\xb67\x16\x97\x1b+\xe3\x8d\xa5\xc9\xd6\xca\x8c\xbb\xb8\xd8\x9c;\xed\x9e\x9ak,\xdfs\xbf>\xda\\xb9\xbeva\xfc\xaf/\xa6\xdc\xb9\xfb\xee\xe2\xb1\xb5\x89\xa9\xc6\xd2\xe3\xf6\xcc\x9c{\xee\xa9\xfbj\xb2\xfd\xf4\xd9__\xdcH\xa5S\xad\x1bw\x1aK\xd3[\x87IG\x1e\x1a\x15\r\xf1\x1b5\x8d\xb1\xaae\xd7\x86R\xbaU\xa9\x19\x15\xe8t\xcc\xcc\xd7\x8a\xdb\xf2\xc6\xa8\xa9\x1bi\xf2\xe5\x03\xb3b\xd6L\xad\x94vt\xaddlc?(k\a\xcdr\xbd\xec\x7f\xc71\x84`\xdaF%o\xd8\x86\x1d\x86i\xe4\xbe4k\xb1z\x05\xcb\x86\x1ehm\xb32\xd2\xbbz\xb1V\xab\xa6\x8d?\xd4\xcd\xd1mC\xff\x91\xfebGz\x97U\xaej53W2B\x8d>\xfdh\x9b\x91\x1f1>\xd0\x8b\xb6U\xee\xc6j\xc4\xb2FJF\xda1kFz\x14:,\x98:\x80\xb0*!\b\x1f\x1f0>gtE\xb3?\xdb\xf7\xefEE\x17?\xfc?\xc5\xacZ\xaf\xabJ\x…………..”

这里面的复杂数据就是压缩后的,我们要看原文件就需要对其进行解压缩

猜你喜欢

转载自blog.csdn.net/qq_41768400/article/details/80564057
今日推荐