数据获取——爬虫(上)
1.初识爬虫(基本概念)
1.1什么是爬虫
传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。
聚焦爬虫的工作流程较为复杂,需要根据一定的网页分析算法过滤与主题无关的链接,保留有用的链接并将其放入等待抓取的URL队列。然后,它将根据一定的搜索策略从队列中选择下一步要抓取的网页URL,并重复上述过程,直到达到系统的某一条件时停止。
另外,所有被爬虫抓取的网页将会被系统存贮,进行一定的分析、过滤,并建立索引,以便之后的查询和检索。

网络爬虫定义:就是模拟客户端发送网络请求,获取响应数据,一种按照一定的规则,自动地抓取万维网信息的程序或脚本。
网络爬虫作用:从万维网上获取, 我们需要的信息。
1.2网页的组成:
网页可以分为三大部分:HTML、CSS 和 JavaScript。如果把网页比作一个人的话,HTML相当于骨架,JavaScript 相当于肌肉,CSS 相当于皮肤,三者结合起来才能形成一个完整的网页。
1.超文本标记语言(英语:HyperText Markup Language,简称:HTML)是一种用于创建网页的标准标记语言。可以使用 HTML 来建立自己的 WEB 站点,HTML 运行在浏览器上,由浏览器来解析。
我们浏览的网页包括文字、按钮、图片和视频等各种复杂的元素,其基础架构就是 HTML。不同类型的元素通过不同类型的标签来表示,如图片用 img标签表示,视频用 video 标签表示,段落用 p 标签表示,它 们之间的布局又常通过布局标签 div嵌套组合而成,各种标签通过不同的排列和嵌套就可以形成网页的框架。我们在 Chrome 浏览器中打开百度,右击并选择 “检查”项(或按 F12 键),打开开发者模式,这时在 Elements 选项卡中即可看到网页的源代码。
2.CSS,全称叫作 Cascading Style Sheets,即层叠样式表。虽然HTML定义了网页的结构,但是只有 HTML页面的布局并不美观。“层叠”是指当在 HTML中引用了数个样式文件,并且样式发生冲突时,浏览器能依据层叠顺序处理。“样式”指网页中文字大小、颜色、元素间距、排列等 格式。CSS 是目前唯一的网页页面排版样式标准,有了它的帮助,页面才会变得更为美观。
3.JavaScript,简称 JS,是一种脚本语言。HTML和 CSS 配合使用,提供给用户的只是一种静态信息,缺乏交互性。我们在网页里可能会看到一些交互和动画效果,如下载进度条、提示框、轮播图等,这通常就是 JavaScript 的功劳。它的出现使得用户与信息之间不只是一种浏览与显示的关系,而是实现了一种实时、动态、交互的页面功能。 JavaScript 通常也是以单独的文件形式加载的,后缀为 js,在 HTML中通过 script 标签即可引入,例如: <script src="jquery-2.1.0.js"></script> 综上所述,HTML定义了网页的内容和结构,CSS 描述了网页的布局,JavaScript 定义了网页的行为。
1.3Robots协议
Robots协议,即Robots Exclusion Standard 网络爬虫排除协议。
作用:网站告知网络爬虫哪些页面可以爬取,哪些不能爬取
形式:在网站根目录下的robots.txt文件

1.4爬虫采集方案分类
通用爬虫捜索引擎抓取系统(Baidu、Google、Yahoo等)的重要组成部分。主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份。但是大多数情况下,网页里面90%的内容对用户来说是无用的。
聚焦爬虫需要根据一定的网页分析算法过滤与主题无关的链接,保留有用的链接并将其放入等待抓取的URL队列。然后,它将根据一定的搜索策略从队列中选择下一步要抓取的网页URL,并重复上述过程,直到达到系统的某一条件时停止。

2.页面请求 - 请求库的使用
2.1requests的使用
requests 是一个优雅而简单的 Python HTTP请求库,requests 的作用是 发送请求获取响应数据。requests 使用3步骤如下:
#1.导入模块 import requests #2.发送get请求, 获取响应: 当我们调用这个方法,并传入对应的 URL就能得到网页的源代码 response = requests.get('https://www.baidu.com/') #3.从响应中获取数据: print(response.text) ''' 步骤三的输出可能会造成乱码 response.encoding = 'utf8' #二进制转换字符使用的编码 print(response.text) print(response.encoding) #响应体 str类型 print(response.content.decode()) # 把二进制数据转换字符串 print(response.content) # 打印响应的二进制数据;响应体 bytes类型 '''
requests库的主要方法:
requests.requst() - > 构造一个请求,最基本的方法,是下面方法的支撑
requests.get() - >获取网页,对应HTTP中的GET方法
requests.post() - > 向网页提交信息,对应HTTP中的POST方法
requests.head() - > 获取html网页的头信息,对应HTTP中的HEAD方法
requests.put() - > 向html提交put方法,对应HTTP中的PUT方法
requests.patch() - > 向html网页提交局部请求修改的的请求,对应HTTP中的PATCH方法
requests.delete() - > 向html提交删除请求,对应HTTP中的DELETE方法
3.页面解析-解析库的使用
3.1正则表达式
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。Python 的re 模块,提供了 Perl 风格的正则表达式模式。re 模块使 Python 语言拥有全部的正则表达式功能。compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。re模块主要函数:

'''re.match函数 match(pattern, string, flags=0) re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。''' print(re.match('www', 'www.baidu.com').span()) # 在起始位置匹配 print(re.match('com', 'www.baidu.com')) # 不在起始位置匹配 #结果 (0, 3) # None '''re.search方法
search(pattern, string, flags=0)
re.search 扫描整个字符串并返回第一个成功的匹配。''' print(re.search('www', 'www.baidu.com').span()) # 在起始位置匹配 print(re.search('com', 'www.baidu.com').span()) # 不在起始位置匹配 #结果 (0, 3) # (10, 13)
re.match与re.search的区别:
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
re.compile 函数
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
pattern = re.compile(r'\d+') # 用于匹配至少一个数字 m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配 print(m) # 结果:<_sre.SRE_Match object; span=(3, 5), match='12'>

findall:在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。注意: match 和 search 是匹配一次 findall 匹配所有。
pattern = re.compile(r'\d+') # 查找数字 result1 = pattern.findall('blue 123 google 456') result2 = pattern.findall('bbb88uuu123google456', 0, 10) print(result1) print(result2)
结果:['123', '456']['88', '12']
re.sub():函数用于替换字符串中的匹配项,如果没有匹配的项则字符串将没有匹配的返回。
#sub(pattern, repl, string, count=0, flags=0) re.sub('\d{4}$','0000','13549876489') #输出 '13549870000' phone = "2021-999-888 # 这是一个奇奇怪怪的电话号码" # 删除字符串中的 Python注释 num = re.sub(r'#.*$', "", phone) print("电话号码是 : ", num) # 删除非数字(-)的字符串 num = re.sub(r'\D', "", phone) print("电话号码是 : ", num)
3.2xpath使用
XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。另外,它还提供了超过 100 个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等。几乎所有我们想要定位的节点,都可以用 XPath 来选择。
XPath 常用规则:
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
示例://title[@lang='eng']
这就是一个 XPath 规则,它代表选择所有名称为 title,同时属性 lang 的值为 eng 的节点。
3.3网站网页解析
#第一步、在制定页面调试浏览器,观察所需的文本内容 #第二步、请求页面 import requests # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36', } # 请求参数 params = ( ('wd', '周杰伦'), ) response = requests.get('https://www.baidu.com/s', headers=headers,params=params) # 获取响应过长,只显示部分内容 response.encoding = 'utf8' print(response.text) #第三步,检查是否有我们要的内容 #第四步、导入解析库,解析内容 from lxml import etree import re html = etree.HTML(response.text) print(html) #第五步、复制xpath : //*[@id="7"]/div/div[2]/div[1] result = html.xpath('//*[@id="7"]/div/div[2]/div[1]') print(result) result = html.xpath('//*[@id="7"]/div/div[2]/div[1]/text()') print(result) result = html.xpath('//*[@id="7"]/div/div[2]/div[1]/em/text()') # 获取了所有的节点的周杰伦 print(result)
此处常遇到的问题:xpath使用相对路径返回值为空!
4.获得页面数据 - 数据存储
4.1存储方式
爬虫请求解析后的数据,需要保存下来,才能进行下一步的处理,一般保存数据的方式有如下几种:
- 文件:txt、csv、excel、json等,保存数据量小。
- 关系型数据库:mysql、oracle等,保存数据量大。
- 非关系型数据库:Mongodb、Redis等键值对形式存储数据,保存数据量大。
- 二进制文件:保存爬取的图片、视频、音频等格式数据。
#保存数据到txt with open('comments.txt', 'w', encoding='utf-8') as f: #使用with open()新建对象f # 将列表中的数据循环写入到文本文件中 for i in comments_list: f.write(i+"\n") #写入数据 #保存数据到csv new_list = [[x] for x in comments_list] # 列表生成器,将列表项转为子列表 with open("comments.csv", mode="w", newline="", encoding="utf-8") as f: csv_file = csv.writer(f) # 创建CSV文件写入对象 for i in new_list: csv_file.writerow(i) # 读取刚刚保存的csv文件 with open('/home/mw/project/comments.csv', 'r', encoding='utf-8') as csvfile: reader = csv.reader(csvfile) for row in reader: print(row) #pandas保存excel、csv df = pd.DataFrame(comments_list) # 把comments_list列表转换为pandas DataFrame df.to_excel('comments.xlsx') # 保存到excel表格 df.to_csv('comments.csv') # 保存在csv文件
4.2实例代码
1 import requests 2 from lxml import etree 4 import requests 5 6 cookies = { 7 'Hm_lvt_1d9b8e4e110b54c48922093ef42f94fe': '1647522958', 8 'PHPSESSID': 'e5ne2vg34tkfkjseuduod1q5ss', 9 'Hm_lpvt_1d9b8e4e110b54c48922093ef42f94fe': '1647523063', 10 'UM_distinctid': '17f9806e4e4886-0e3b4c1d996d63-977173c-1fa400-17f9806e4e535f', 11 'CNZZDATA1278227787': '951014879-1647514960-%7C1647514960', 12 } 13 14 headers = { 15 'Connection': 'keep-alive', 16 'Cache-Control': 'max-age=0', 17 'Upgrade-Insecure-Requests': '1', 18 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36', 19 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 20 'Accept-Language': 'zh-CN,zh;q=0.9', 21 } 22 23 response = requests.get('http://hot.meibp.com/', headers=headers, cookies=cookies, verify=False) 24 25 html = etree.HTML(response.text) 26 27 divs = html.xpath('//div[@class="items"]/div[@class="row"]/div') 28 29 for div in divs: 30 cat = div.xpath('./a/@title') 31 for a in div.xpath('./div/div/a'): 32 result = { 33 "热搜类别": "".join(cat), 34 "标题": "".join(a.xpath('./@title')), 35 "链接": "".join(a.xpath('./@href')) 36 } 37 print(result) 38 39 import json 40 with open('data.json', 'w') as file: 41 file.write(json.dumps(results, indent=2))

浙公网安备 33010602011771号