欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spider第1课:爬虫基础

程序员文章站 2024-01-25 08:37:17
...

爬虫准备

本节包含以下知识

  • urlopen
  • chardet
  • response
  • parse
  • post

爬虫的介绍

  • 爬虫定义:网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本
  • 两大特征:
    • 能按作者要求下载数据或者内容
    • 能自动在网络上流窜
  • 三大步骤:
    • 下载网页
    • 提取想要的信息
    • 根据一定规则自动跳到另外的网页上执行上面两步内容
  • 爬虫分类
    • 通用爬虫
    • 专用爬虫(聚焦爬虫)
  • Python网络包简介
    • Python2.X:urllib,urllib2,urllib3,httplib,httplib2,request
    • Python3.X:urllib,urllib3,httplib,request
    • Python2基本使用urllib和urllib2或者request即可
    • Python3基本使用urllib和request

urllib

包含模块

  • urllib.request:打开和读取urls
  • urllib.error:包含urllib.request产生的常见的错误,使用try捕捉
  • urllib.parse:包含一些解析url的方法
  • urllib.robotparse:解析robots.txt文件,网站一些针对爬虫的规矩会写在这里面,比如能爬的有哪些不能爬的有哪些,或者告诉你,你一天只能爬一次。
  • 案例见1.py
    • 在这个案例实现过程中遇到的几个小问题:
      • 有的网址爬的很慢有的网址爬的很快,这可能和网站服务器有关,一开始我还以为是我电脑太辣鸡(或许可能就是我电脑辣鸡…)也可能和所爬取网页的内容量有关,因为我爬的是海贼王漫画的网址。
      • bytes转换成字符串需要decode,网页的不同可能会抛出这样的异常:UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xba in position 244: invalid start byte
        这种情况我们可以打开控制台,看一下网页结构,在head中找meta标签,看一下charset属性值,通常是utf-8但是也有国际码gbk的,所以将decode第一个参数传入gbk(这是我爬的网址的charset属性值):decode(“gbk”),我试了一下就报了这样的错,这样一般是decode第二个参数的errors为严格(strict)形式造成的,因为默认strict,最后写成decode(“gbk”,“ignore”)这样问题解决。

Spider第1课:爬虫基础

Spider第1课:爬虫基础

from urllib import request
if __name__ == '__main__':
    url = "http://manhua.kukudm.com/comiclist/4/index.htm"
    # 打开相应url并把相应页面座位返回
    rsp = request.urlopen(url)

    # 把返回结果读取出来,这里读取出来的是bytes
    html = rsp.read()
    print(type(html))

    # 把bytes转换成字符串需要解码
    html = html.decode("gbk","ignore")
    print(html)
# 由于打印出的html篇幅较大,此代码块就不运行了

网页编码问题解决(我透…原来下节课就讲了,我还在上边自己研究…)

  • 来看一下大佬解法吧
  • chardet:可以检测页面文件的编码格式,但是可能有误(淦,妙啊…)
from urllib import request
import chardet
if __name__ == "__main__":
    url = "https://blog.csdn.net/c406495762/article/details/72858983"
    # 用request打开url
    rsp = request.urlopen(url)
    # 下载html
    html = rsp.read()
    # 解码
    # 通过chardet检测编码
    cs = chardet.detect(html)
    print(type(cs))
    print(cs)
    
    # 返回一个字典
    # 通过get方法获取编码方式,并添加默认编码utf-8
    html = html.decode(cs.get("encoding","utf-8"))
    # print(html)
<class 'dict'>
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}

可以看见返回的是一个dict字典

  • 使用get取值保证不会出错

urlopen的返回对象

  • geturl:返回请求对象的url
  • info:请求反馈对象的meta信息
  • getcode:返回的是http code如:404 500等
from urllib import request
import chardet

if __name__ == "__main__":
    url = "https://blog.csdn.net/c406495762/article/details/72858983"
    rsp = request.urlopen(url)
    
    print("URL:{}".format(rsp.geturl()))
    print("Info:{}".format(rsp.info()))
    print("Code:{}".format(rsp.getcode()))  # 200表示成功
    
    html = rsp.read()
    cs = chardet.detect(html)
    html = html.decode(cs.get("encoding", "utf-8"))
    
URL:https://blog.csdn.net/c406495762/article/details/72858983
Info:Server: openresty
Date: Wed, 26 Aug 2020 11:20:23 GMT
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Connection: close
Vary: Accept-Encoding
Set-Cookie: uuid_tt_dd=10_37483366610-1598440823981-482990; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Set-Cookie: dc_session_id=10_1598440823981.275301; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Content-Language: en-US
Strict-Transport-Security: max-age= 31536000


Code:200

request.date的使用

  • 访问网络的两种方法
    • get
      • 利用参数给服务器传递信息
      • 参数为dict,然后用parse编码
    • post
      • 一般向服务器传递参数使用
      • post是把信息自动加密处理
      • 如果我们想使用post信息,需要用到urlopen中的data参数
      • 使用post,意味着http的请求头部可能需要更改:
        • Content-Type:application/x-www.form-urlencode
        • Content-Length:数据长度
        • 一旦更改请求方式,要注意其他请求的头部信息要相适用
      • urllib.parse.urlencode可以将字符串自动转换成上面的格式

我们百度药水哥时,url中的wd是可视的“wd=药水哥”
Spider第1课:爬虫基础

但是调出调试器可以看到向服务器发送请求时药水哥是已经经过编码了的,获取方式是get
Spider第1课:爬虫基础

# parse使用方法
# 掌握对url进行编码的方法
from urllib import request,parse
import chardet
if __name__ == "__main__":
    base_url = "https://baidu.com/s?"
    wd = input("请输入关键字:")
    # 要想使用data需要使用字典类型结构
    qs = {
        "wd":wd
    }
    # 转换url编码
    qs = parse.urlencode(qs)
    print(qs)
    fullurl = base_url + qs
    print(fullurl)
    # 用request打开url
    rsp = request.urlopen(fullurl)
    html = rsp.read()
    cs = chardet.detect(html)
    html = html.decode(cs.get("encoding","utf-8"))
    #print(html)  篇幅有限不打印了  
请输入关键字:药水哥
wd=%E8%8D%AF%E6%B0%B4%E5%93%A5
https://baidu.com/s?wd=%E8%8D%AF%E6%B0%B4%E5%93%A5

利用parse模块模拟post请求

分析百度翻译

  • 步骤:
    • 打开F12
    • 尝试输入单词girl,发现每敲一个字母后都有请求
    • 请求的地址是 http://anyi.baidu.com/sug
    • 利用NetWork-All-Header 发现FormData的值是kw:girl
    • 检查返回的内容格式,发现返回的是json格式内容——>需要使用json包

Spider第1课:爬虫基础

遇到的一些问题

  • form data中有“kw:girl”的键是会变的,比如中午还是kw,晚上就变成了query
    Spider第1课:爬虫基础

  • post的方式也会变,我中午看的是sug,晚上变成了langdetect
    Spider第1课:爬虫基础

  • 百度翻译的爬虫errno错误码解释:

    • errno:997 cookie失效
    • errno:998 sign参数错误
    • errno:999 post未提交from,to参数
    • error:1000 需要翻译的字符串为空 # 记住,这个是error,也许是百度发现自己的errno拼错了吧
  • 返回{‘error’: 0, ‘msg’: ‘success’, ‘lan’: ‘en’}错误

    • 应该是被识别出是爬虫了,下面那个网址上有一些介绍
    • 需要过一段时间才能恢复正常,被识别出爬虫后IP会被屏蔽一段时间

总之爬虫千变万化,这些对于新手来说很容易掉坑里

有助于理解的网页参考

以下为urlopen示例代码

from urllib import request,parse
import json
'''
1.利用data构造内容,然后urlopen打开
2.返回一个json格式的结果
3.结果就应该是girl的释义 
'''
# 基础url
baseurl = "https://fanyi.baidu.com/sug"
# 存放用来模拟form的数据一定是dict格式
data = {
    "kw":"girl"
}
# 需要用parse模块对data进行编码
# 这里的data是str格式,要在后面加一个encode编码成bytes格式
data = parse.urlencode(data).encode()

# 现在构造一个请求头,请求头应该至少包含传入的数据长度
# request要求传入的请求头是一个dict格式
# 我们使用的是urlopen,参数中没有headers,所以下面这个写出来用不到
# 但是使用requests模块时需要。使用requests时百度翻译好像会将没有headers的请求头识别为爬虫
headers = {
    # 因为使用post,至少应该包含一个content-length字段
    # 大小写敏感
    # 具体根据控制台上显示的写(我是这么认为的,我也不晓得对不对。。)
    "content-type":"application/x-www-form-urlencoded; charset=UTF-8",
    'Content-Length':len(data),
    "Cookie":"BAIDUID=245363281C029E0C89B1BFDBC01DE66E:FG=1; BIDUPSID=245363281C029E0C89B1BFDBC01DE66E; PSTM=1598441364; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1598613294,1598670639; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1598670639; __yjsv5_shitong=1.0_7_665df460886d847b8939c7df8c35c6e7bc95_300_1598670636937_112.24.42.176_6d2dbfb2; yjs_js_security_passport=5a0f8f39a6a6b3bc53428e0089e9dc728204aadd_1598670637_js"
}

# 现在有了url,form data,headers就可以发起一个请求了,这里urlopen没有headers参数
rsp = request.urlopen(baseurl, data=data)
json_data = rsp.read().decode()

#把json字符串转换成字典
json_data = json.loads(json_data)
print(json_data)
{'errno': 0, 'data': [{'k': 'girl', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;'}, {'k': 'girls', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数;'}, {'k': 'girlfriend', 'v': 'n. 女朋友; 女情人; (女子的)女伴,女友;'}, {'k': 'girl friend', 'v': ' 未婚妻; 女性朋友;'}, {'k': "Girls' Generation", 'v': ' 少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合);'}]}
# 字典类型数据读取
for item in json_data["data"]:
    print(item["k"],"——",item["v"])
girl —— n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;
girls —— n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数;
girlfriend —— n. 女朋友; 女情人; (女子的)女伴,女友;
girl friend ——  未婚妻; 女性朋友;
Girls' Generation ——  少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合);

urlopen中不存在headers参数【pycharm中按住ctrl+鼠标左键点击urlopen可以查看源码】

Spider第1课:爬虫基础

为了更多的设置请求信息,单纯的通过urlopen函数已经不太好用了

  • 需要利用request.Request类
    • request可以允许我们无限制的设置headers请求头,可以近乎完美的模拟浏览器请求,这样远程服务器就很难发现我们是爬虫的身份
    • 上面的示例使用request实现代码如下:
# 此处代码和上面一样,用Jupyter笔记本这一段代码删掉也可以借助上面的代码运行

# 构造一个Request实例
req = request.Request(url=baseurl,data=data,headers=headers)

# 因为已经构造了一个Request请求实例,则所有请求信息都可以封装在Request实例中
# 现在有了url,form data,headers就可以发起一个请求了,这里urlopen没有headers参数
rsp = request.urlopen(req)
json_data = rsp.read().decode()

#把json字符串转换成字典
json_data = json.loads(json_data)
for item in json_data["data"]:
    print(item["k"],"——",item["v"])
girl —— n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;
girls —— n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数;
girlfriend —— n. 女朋友; 女情人; (女子的)女伴,女友;
girl friend ——  未婚妻; 女性朋友;
Girls' Generation ——  少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合);
相关标签: python全栈 python