文章目录
Day01笔记
概述
【1】定义
1.1) 网络蜘蛛、网络机器人,抓取网络数据的程序
1.2) 其实就是用Python程序模仿人点击浏览器并访问网站,而且模仿的越逼真越好
【2】爬取数据的目的
2.1) 公司项目的测试数据,公司业务所需数据
2.2) 获取大量数据,用来做数据分析
【3】企业获取数据方式
3.1) 公司自有数据
3.2) 第三方数据平台购买(数据堂、贵阳大数据交易所)
3.3) 爬虫爬取数据
【4】Python做爬虫优势
4.1) Python :请求模块、解析模块丰富成熟,强大的Scrapy网络爬虫框架
4.2) PHP :对多线程、异步支持不太好
4.3) JAVA:代码笨重,代码量大
4.4) C/C++:虽然效率高,但是代码成型慢
【5】爬虫分类
5.1) 通用网络爬虫(搜索引擎使用,遵守robots协议)
robots协议: 网站通过robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,通用网络爬虫需要遵守robots协议(君子协议)
示例: https://www.baidu.com/robots.txt
5.2) 聚焦网络爬虫 :自己写的爬虫程序
【6】爬取数据步骤
6.1) 确定需要爬取的URL地址
6.2) 由请求模块向URL地址发出请求,并得到网站的响应
6.3) 从响应内容中提取所需数据
a> 所需数据,保存
b> 页面中有其他需要继续跟进的URL地址,继续第2步去发请求,如此循环
-
重大问题思考
网站如何来判定是人类正常访问还是爬虫程序访问?–检查请求头!!!
# 请求头(headers)中的 User-Agent # 测试案例: 向测试网站http://httpbin.org/get发请求,查看请求头(User-Agent) import requests url = 'http://httpbin.org/get' res = requests.get(url=url) html = res.text print(html) # 请求头中:User-Agent为-> python-requests/2.22.0 那第一个被网站干掉的是谁???我们是不是需要发送请求时重构一下User-Agent???添加 headers 参数!!!
-
重大问题解决
""" 包装好请求头后,向测试网站发请求,并验证 养成好习惯,发送请求携带请求头,重构User-Agent User-Agent参数详解 """ import requests url = 'http://httpbin.org/get' headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1'} html = requests.get(url=url,headers=headers).content.decode('utf-8','ignore') # 'ignore' 忽略无法转码的字符串 防止网页中带有无法识别字符串而报错 # Uni codeDecodeError: utf-8 xxx cannot decode char \xxx in. ignore 可解决 # UnicodeEncodeError: gbk code cannot encode char \xxx in, windows 写入文件时常报错误 # with open('xxx. txt', 'w’, encoding='gb18030') as f: print(html)
-
小总结
【1】 什么是robots协议,爬虫分为通用网络爬虫和聚焦网络爬虫,只有通用爬虫需要遵守协议 【2】 requests模块使用 res = requests.get(url=url,headers={'User-Agent':'xxx'}) 响应对象res属性: a> res.text # 字符串文本 b> res.content # 二进制文本 c> res.status_code # 响应码 d> res.url # 真实url 【3】网站乱码解析 方法1: res = requests.get(url=url, headers=headers) res.encoding = 'utf-8' file.write(res.text) 方法2: # 推荐使用方式 获取bytes数据,手动转码 requests.get(url=url, headers=headers).content.decode('utf-8')
正则解析模块re
re模块使用流程
# 方法一
r_list=re.findall('正则表达式',html,re.S)
# re.S 让正则的.能够匹配\n换行符
# 方法二
pattern = re.compile('正则表达式',re.S)
r_list = pattern.findall(html)
-
思考 - 请写出匹配任意一个字符的正则表达式?
import re # 方法一 pattern = re.compile('[\s\S]') result = pattern.findall(html) # 方法二 pattern = re.compile('.',re.S) result = pattern.findall(html)
-
代码示例
import re html = ''' <div><p>九霄龙吟惊天变</p></div> <div><p>风云际会潜水游</p></div> ''' # 贪婪匹配 p = re.compile('<div><p>.*</p></div>',re.S) r_list = p.findall(html) print(r_list) # 非贪婪匹配 p = re.compile('<div><p>.*?</p></div>',re.S) r_list = p.findall(html) print(r_list)
正则表达式分组
-
作用
在完整的模式中定义子模式,将每个圆括号中子模式匹配出来的结果提取出来
-
示例代码
import re s = 'A B C D' p1 = re.compile('\w+\s+\w+') print(p1.findall(s)) # 分析结果是什么??? # ['A B', 'C D'] p2 = re.compile('(\w+)\s+\w+') print(p2.findall(s)) # 第一步: ['A B', 'C D'] # 第二步: ['A', 'C'] p3 = re.compile('(\w+)\s+(\w+)') print(p3.findall(s)) # 第一步: ['A B', 'C D'] # 第二步: [('A','B'), ('C','D')]
-
分组总结
1、在网页中,想要什么内容,就加() 2、先按整体正则匹配,然后再提取分组()中的内容 如果有2个及以上分组(),则结果中以元组形式显示 [(),(),()]
-
课堂练习
# 从如下html代码结构中完成如下内容信息的提取: 问题1 :[('Tiger',' Two...'),('Rabbit','Small..')] 问题2 : 动物名称 :Tiger 动物描述 :Two tigers two tigers run fast ********************************************** 动物名称 :Rabbit 动物描述 :Small white rabbit white and white
-
页面结构如下
<div class="animal"> <p class="name"> <a title="Tiger"></a> </p> <p class="content"> Two tigers two tigers run fast </p> </div> <div class="animal"> <p class="name"> <a title="Rabbit"></a> </p> <p class="content"> Small white rabbit white and white </p> </div>
-
练习答案
import re html = '''<div class="animal"> <p class="name"> <a title="Tiger"></a> </p> <p class="content"> Two tigers two tigers run fast </p> </div> <div class="animal"> <p class="name"> <a title="Rabbit"></a> </p> <p class="content"> Small white rabbit white and white </p> </div>''' p = re.compile('<div class="animal">.*?title="(.*?)".*?content">(.*?)</p>.*?</div>',re.S) r_list = p.findall(html) for rt in r_list: print('动物名称:',rt[0].strip()) # strip() 去掉字符串两头的空白包括\n\t和空格 print('动物描述:',rt[1].strip()) print('*' * 50) # 把想要提取的数据先复制出来,再按需删除,删除的地方加上.*?,需要提取的地方加(.*?)
猫眼电影top100抓取案例
-
爬虫需求
【1】确定URL地址 百度搜索 - 猫眼电影 - 榜单 - top100榜 【2】 爬取目标 所有电影的 电影名称、主演、上映时间
-
爬虫实现
【1】查看网页源码,确认数据来源 响应内容中存在所需抓取数据 - 电影名称、主演、上映时间 【2】翻页寻找URL地址规律 第1页:https://maoyan.com/board/4?offset=0 第2页:https://maoyan.com/board/4?offset=10 第n页:offset=(n-1)*10 【3】编写正则表达式 <div class="movie-item-info">.*?title="(.*?)".*?class="star">(.*?)</p>.*?releasetime">(.*?)</p> 【4】开干吧兄弟
-
代码实现
""" 猫眼电影top100抓取(电影名称、主演、上映时间) """ import requests import re import time import random class MaoyanSpider: def __init__(self): self.url = 'https://maoyan.com/board/4?offset={}' self.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'} def get_html(self, url): html = requests.get(url=url, headers=self.headers).text # 直接调用解析函数 self.parse_html(html) def parse_html(self, html): """解析提取数据""" regex = '<div class="movie-item-info">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>' pattern = re.compile(regex, re.S) r_list = pattern.findall(html) # r_list: [('活着','牛犇','2000-01-01'),(),(),...,()] self.save_html(r_list) def save_html(self, r_list): """数据处理函数""" item = {} for r in r_list: item['name'] = r[0].strip() item['star'] = r[1].strip() item['time'] = r[2].strip() print(item) def run(self): """程序入口函数""" for offset in range(0, 91, 10): url = self.url.format(offset) self.get_html(url=url) # 控制数据抓取频率:uniform()生成指定范围内的浮点数 time.sleep(random.uniform(0,1)) if __name__ == '__main__': spider = MaoyanSpider() spider.run()
数据持久化 - MySQL
-
pymysql回顾
import pymysql db = pymysql.connect('localhost','root','123456','maoyandb',charset='utf8') cursor = db.cursor() ins = 'insert into filmtab values(%s,%s,%s)' cursor.execute(ins,['霸王别姬','张国荣','1993']) db.commit() cursor.close() db.close()
-
练习 - 将电影信息存入MySQL数据库
【1】提前建库建表 mysql -h127.0.0.1 -uroot -p123456 create database maoyandb charset utf8; use maoyandb; create table maoyantab( name varchar(100), star varchar(300), time varchar(100) )charset=utf8; 【2】 使用excute()方法将数据存入数据库思路 2.1) 在 __init__() 中连接数据库并创建游标对象 2.2) 在 save_html() 中将所抓取的数据处理成列表,使用execute()方法写入 2.3) 在run() 中等数据抓取完成后关闭游标及断开数据库连接
-
汽车之家二手车信息抓取
【1】URL地址 进入汽车之家官网,点击 二手车 即:https://www.che168.com/beijing/a0_0msdgscncgpi1lto1cspexx0/ 【2】抓取目标 每辆汽车的 2.1) 汽车名称 2.2) 行驶里程 2.3) 城市 2.4) 个人还是商家 2.5) 价格 【3】抓取前5页
-
参考答案
import requests import re import time import random class CarSpider: def __init__(self): self.url = 'https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp{}exx0/?pvareaid=102179#currengpostion' self.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'} def get_html(self, url): html = requests.get(url=url, headers=self.headers).content.decode('gb2312', 'ignore') self.parse_html(html) def parse_html(self, html): pattern = re.compile('<li class="cards-li list-photo-li".*?<div class="cards-bottom">.*?<h4 class="card-name">(.*?)</h4>.*?<p class="cards-unit">(.*?)</p>.*?<span class="pirce"><em>(.*?)</em>', re.S) car_list = pattern.findall(html) self.save_html(car_list) def save_html(self, car_list): for car in car_list: print(car) def run(self): for i in range(1,6): page_url = self.url.format(i) self.get_html(page_url) time.sleep(random.randint(1,2)) if __name__ == '__main__': spider = CarSpider() spider.run()
请求模块(requests)
html = requests.get(url=url,headers=headers).text
html = requests.get(url=url,headers=headers).content.decode('utf-8')
with open('xxx.txt','w',encoding='utf-8') as f:
f.write(html)
解析模块(re)
-
使用流程
p = re.compile('正则表达式',re.S) r_list = p.findall(html)
-
贪婪匹配和非贪婪匹配
贪婪匹配(默认) : .* 非贪婪匹配 : .*?
-
正则表达式分组
【1】想要什么内容在正则表达式中加() 【2】多个分组,先按整体正则匹配,然后再提取()中数据。结果:[(),(),(),(),()]
抓取步骤
【1】确定所抓取数据在响应中是否存在(右键 - 查看网页源码 - 搜索关键字)
【2】数据存在: 查看URL地址规律
【3】写正则表达式,来匹配数据
【4】程序结构
a>每爬取1个页面后随机休眠一段时间
# 程序结构
class xxxSpider(object):
def __init__(self):
# 定义常用变量,url,headers及计数等
def get_html(self):
# 获取响应内容函数,使用随机User-Agent
def parse_html(self):
# 使用正则表达式来解析页面,提取数据
def save_html(self):
# 将提取的数据按要求保存,csv、MySQL数据库等
def run(self):
# 程序入口函数,用来控制整体逻辑
if __name__ == '__main__':
# 程序开始运行时间戳
start = time.time()
spider = xxxSpider()
spider.run()
# 程序运行结束时间戳
end = time.time()
print('执行时间:%.2f' % (end-start))