网络爬虫是一种按照一定规则自动抓取万维网信息的程序. 在如今网络发展, 信息爆炸的时代, 信息的处理变得尤为重要. 而这之前就需要获取到数据. 有关爬虫的概念可以到网上查看详细的说明, 今天在这里介绍一下使用 urllib 进行网络爬虫的方法使用, 在最后的一个案例中把最基本的爬虫要素运用进去, 可以作为初学者的一个模板, 读懂它进行适当修改就可以使用.
以我的经验来看, 在编程上对于陌生的简单的东西, 最快的学习方法就是从代码入手了. 当然有些很厉害的兄弟, 可以完全忽略我这篇博客了. 下面的内容我尽量将注释写在代码当中.
1,urllib 爬取网页
下面是通过 urllib 的 request 函数来获取网页信息, 现在的 request 库也很方便, 不过原理都是一样的.
- import urllib.request
- # 向指定的 url 地址发送请求并返回服务器响应的数据 (文件的对象)
- response = urllib.request.urlopen("http://www.baidu.com")
- # 读取文件的全部内容, 会把读到的东西赋值给一个字符串变量
- data = response.read()
- print(data) # 读取得到的数据
- print(type(data)) # 查看数据类型
- # 读取一行
- data = response.readline()
- # 读取文件的全部内容, 赋值给一个列表变量, 优先选择
- data = response.readlines()
- # print(data)
- print(type(data[100]))
- print(type(data[100].decode("utf-8"))) # 转字符串
- print(len(data))
- # 将爬取到的网页写入文件
- with open(r"F:/python_note / 爬虫 / file/file1.html", "wb") as f:
- f.write(data)
- # response 属性
- # 返回当前环境的有关信息
- print(response.info())
- # 返回状态码
- print(response.getcode())
- # 200 为正常, 304 位为有缓存
- # 返回当前正在爬取的 url 地址
- print(response.geturl())
- url = "https://www.sogou.com/sgo?query = 凯哥学堂 & hdq=sogou-wsse-16bda725ae44af3b-0099&lxod=0_16_1_-1_0&lxea=2-1-D-9.0.0.2502-3-CN1307-0-0-2-E96F3D19F4C66A477CE71FD168DD223D-62&lxoq=kaigexuetang&lkx=0&IE=utf8"
- url2 = r"https://www.sogou.com/sgo?query=凯哥学堂&hdq=sogou-wsse-16bda725ae44af3b-0099&lxod=0_16_1_-1_0&lxea=2-1-D-9.0.0.2502-3-CN1307-0-0-2-E96F3D19F4C66A477CE71FD168DD223D-62&lxoq=kaigexuetang&lkx=0&ie=utf8"
- newurl = urllib.request.quote(url) # 将含汉字的编码
- print(newurl)
- newurl2 = urllib.request.unquote(url2) # 解码
- print(newurl2)
- # 端口号, http 80
- # https 443
2, 爬取到的网页直接写入文件
将网页信息写入文件可以通过上面的读取然后再写入文件, 还有更简便的方法, 就是爬取页面的同时写入文件, 这个也不难, 只是一个函数而已. 相信应该可以明白下面的内容, filename 后面的内容就是需要存储网页信息的文件路径.
- import urllib.request
- urllib.request.urlretrieve("http://www.baidu.com",
- filename=r"F:/python_note / 爬虫 / file/file2.HTML")
- # urlretrieve 在执行过程中, 会产生一些缓存
- # 清除缓存
- urllib.request.urlcleanup()
3, 模拟浏览器
我们都知道, 进行爬虫的时候, 很在乎它的效率, 计算机进行获取数据当然会比手动来的快. 但是这样一来, 你就占用了该网站的大部分带宽, 导致其他人上网会很卡. 因此, 很多网站会有自己的反爬机制, 有些只是简单的预防一下. 通过网络爬虫进行访问时会有一个爬虫的请求头, 那么, 可以模拟一下浏览器来访问, 也就是把请求头中的信息换成浏览器信息. 网上可以找到很多, 随便粘贴一个就好了.
- import random
- import urllib.request
- url = "http://www.baidu.com"
- '''
- # 模拟请求头 (这是一种方法, 下面使用另一种方法)
- headers = {
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) ApplewebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36"
- }
- # 设置一个请求体
- req = urllib.request.Request(url, headers=headers)
- # 发起请求
- response = urllib.request.urlopen(req)
- data = response.read()
- print(data)
- '''
- agentsList = [
- "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60",
- "Opera/8.0 (Windows NT 5.1; U; en)",
- "Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50",
- "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50",
- "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"
- ]
- agentStr = random.choice(agentsList) # 这里是从列表中随机取出一个浏览器信息来改写请求头, 避免被网站发现同一个地址快速持续的访问它而被封掉
- req = urllib.request.Request(url) # 里面添加头要写成键值对
- # 向请求体里添加了 User-Agent
- req.add_header("User-Agent", agentStr) # 这里添加时传入两个字符串, 自动组合
- response = urllib.request.urlopen(req)
- print(response.read())
- 4, 设置超时
- 当访问一直没有被响应时, 我们需要让它继续往下进行而不是卡在那里. 通过异常捕获来实现.
- import urllib.request
- # 如果网页长时间未响应, 系统判断超时, 无法爬取
- for i in range(1, 100):
- try:
- response = urllib.request.urlopen("http://www.baidu.com", timeout=0.1)
- print(len(response.read()))
- except:
- print("time out")
- 5,http 请求
- 当然有些网站还需要先登录之类的, 也就是不仅仅从上面获取信息, 还需要上传一些东西, 下面介绍一下发送请求的两种方法.
- '''
- 使用场景: 进行客户端与服务端之间的消息传递时使用
- GET: 通过 url 网址传递信息, 可以直接在 url 网址上添加要传递的信息 (不安全)
- POST: 可以向服务器提交数据, 是一种比较流行, 安全的数据传递方式
- PUT: 请求服务器存储一个资源, 通常要指定存储的位置
- DELETE: 请求服务器删除一个资源
- HEAD: 请求获取对应的 http 报头信息
- OPTIONS: 可以获取当前 url 所支持的请求类型
- '''
- '''
- get 请求:
- 特点: 把数据拼接到请求路径后面传递给服务器
- 优点: 速度快
- 缺点: 承载的数据量小, 不安全
- '''
- '''
- post 请求:
- 特点: 把参数进行打包, 单独传输
- 优点: 数量大, 安全 (当对服务器数据进行修改时建议使用 post)
- 缺点: 速度慢
- '''
- import urllib.request
- import urllib.parse # 对请求打包的库
- url = "http://www.baidu.com"
- # 将要发送的数据合成一个字典
- # 字典的键去网址里找, 一般为 input 标签的 name 属性的值
- data = {
- "username": "xiaoxiao",
- "passwd": "999"
- }
- # 对要发送的数据进行打包
- postdata = urllib.parse.urlencode(data).encode("utf-8")
- # 请求体
- req = urllib.request.Request(url, data=postdata)
- # 请求
- response = urllib.request.urlopen(req)
- print(response.data())
- 6,JSON 数据解析
- '''
- 概念: 一种保存数据的格式
- 作用: 可以保存本地的 JSON 文件, 也可以将 JSON 串进行传输, 通常将 JSON 称为轻量级的传输方式
- xml 可读性更强, 但是有很多没有用的标签
- JSON 文件组成:
- {} 代表对象 (字典)
- [] 代表列表
- : 代表键值对
- , 分隔两个部分
- '''
- import JSON
- # 将 JSON 格式的字符串转换为 Python 数据类型的对象
- jsonStr = '{"name":"xiaoxiao","age":18,"hobby":["money","power","english"],"parames":{"a":1,"b":2}}'
- jsonData = JSON.loads(jsonStr)
- print(jsonData)
- print(type(jsonData))
- print(jsonData["hobby"])
- jsonData2 = {"name": "xiaoxiao", "age": 18, "hobby": [
- "money", "power", "english"], "parames": {"a": 1, "b": 2}}
- # python 类型的数据就是比 JSON 格式少个引号
- # 将 Python 数据类型的对象转换为 JSON 格式的字符串
- jsonStr2 = JSON.dumps(jsonData2)
- print(jsonStr2)
- print(type(jsonStr2))
7, 抓取网页动态 Ajax 请求的数据
经常浏览一些网页会有这种情况, 就是首先加载出一个页面的内容, 向下滚动还有内容, 在最下面会有一个提示下拉获取更多内容这类的东西. 这个可以让加载网页的速度更快, 毕竟内容少了嘛, 再我们想要看到更多信息时候再加载. 对于这样的一部分页面爬取其实也很简单, 细心观察一下每次多加载一块的页面时, 这时候上方的网址变化可以发现, 有些是有数字变化的. 可以根据里面的具体规律来修改每次请求的信息.
- import urllib.request
- import ssl
- import JSON
- def ajaxCrawler(url):
- headers = {
- "User-Agent": "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
- }
- req = urllib.request.Request(url, headers=headers)
- # 使用 ssl 创建未验证的上下文
- context = ssl._create_unverified_context() # 访问的是 HTTPS
- response = urllib.request.urlopen(req, context=context)
- jsonStr = response.read().decode("utf-8")
- print(type(response.read())) # byte 型
- print(type(response.read().decode("utf-8"))) # JSON 字符串型
- jsonData = JSON.loads(jsonStr)
- return jsonData
- # url = "https://movie.douban.com/j/chart/top_list?type=17&interval_id=100:90&action=&start=0&limit=20"
- # info = ajaxCrawler(url)
- # print(info)
- for i in range(1, 11):
- url = "https://movie.douban.com/j/chart/top_list?type=17&interval_id=100:90&action=&start=" + \
- str(i * 20) + "&limit=20"
- info = ajaxCrawler(url)
- print(len(info))
8, 糗事百科爬虫
- import urllib.request
- import re
- # https://www.qiushibaike.com/text/page/2/
- def jokeCrawler(url):
- headers = {
- "User-Agent": "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
- }
- req = urllib.request.Request(url, headers=headers)
- response = urllib.request.urlopen(req)
- HTML = response.read().decode("utf-8")
- # print(type(HTML))
- pat = r'<div class="author clearfix">(.*?)<span class="stats-vote"><i class="number">'
- re_joke = re.compile(pat, re.S) # re.S 使可以匹配换行
- divsList = re_joke.findall(HTML)
- # print(divsList)
- # print(len(divsList))
- dic = {}
- for div in divsList:
- # 用户名
- re_u = re.compile(r"<h2>(.*?)</h2>", re.S)
- username = re_u.findall(div)
- username = username[0].rstrip()
- # print(username)
- # 段子
- re_d = re.compile(r'<div class="content">\n<span>(.*?)</span>', re.S)
- duanzi = re_d.findall(div)
- duanzi = duanzi[0].strip()
- # print(type(duanzi))
- dic[username] = duanzi
- return dic
- # with open(r"F:/python_note / 爬虫 / file/file2.HTML", "w") as f:
- # f.write(HTML)
- url = "https://www.qiushibaike.com/text/page/1/"
- info = jokeCrawler(url)
- for k, v in info.items():
- print(k + ':\n' + v)
来源: https://www.cnblogs.com/yudanqu/p/9733502.html