爬虫请求模块
1、模块名:urllib.request 2、导入方式: 1、import urllib.request 2、from urllib import request
1、URL:需要爬取的URL地址
2、timeout: 设置等待超时时间,指定时间内未得到响应抛出超时异常
# 导入请求模块(python标准库模块) import urllib.request url = 'http://www.baidu.com/' # 向百度发请求,得到响应对象 response = urllib.request.urlopen(url) # 获取响应对象内容(网页源代码) # read() 是bytes类型 需要decode()转换成string
# 读取百度页面HTML内容 print(response.read().decode('utf-8'))
提取相应对象
1、bytes = response.read() #read()结果为bytes类型 2、string = response.read().decode('utf-8') #read().decode()结果为string类型 3、url = response.geturl() #返回实际数据的url地址(重定向问题) 4、code = response.getcode() #返回http响应码 # 补充 5、string.encode() #转字节串bytes类型 6、bytes.decode() #转字符串string类型
思考:网站如何来判定是人类正常访问还是爬虫程序访问???
# 向测试网站: http://httpbin.org/get 发请求,通过获取响应内容查看自己请求头 import urllib.request url = 'http://httpbin.org/get' response = urllib.request.urlopen(url) print(response.read().decode('utf-8')) # 结果中请求头中的User-Agent竟然是: "Python-urllib/3.7"!!!!!!!!!
# 我们需要重构User-Agent
1、URL:请求的URL地址
2、headers:添加请求头(爬虫和反爬虫斗争的第一步)
#1、构造请求对象(重构User-Agent) req = urllib.request.Reuqest( url='http://httpbin.org/get', headers={'User-Agent':'Mozilla/5.0'} ) #2、发请求获取响应对象(urlopen) res = urllib.request.urlopen(req) #3、获取响应对象内容 html = res.read().decode('utf-8')
from urllib import request # 定义常用变量 url = 'http://httpbin.org/get' headers = {'User-Agent':'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)'} # 1.构建请求对象 req = request.Request(url,headers=headers) # 2.发请求获取响应对象 res = request.urlopen(req) # 3.读取响应对象内容 html = res.read().decode('utf-8') print(html)
# 模块名 urllib.parse # 导入 import urllib.parse from urllib import parse
编码前:https://www.baidu.com/s?wd=美女
编码后:https://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3
# 查询参数:{'wd' : '美女'} # urlencode编码后:'wd=%e7%be%8e%e5%a5%b3' # 示例代码 query_string = {'wd' : '美女'} result = parse.urlencode(query_string) # result: 'wd=%e7%be%8e%e5%a5%b3'
(2)URL地址中 ==多个== 查询参数
from urllib import parse query_string_dict = { 'wd' : '美女', 'pn' : '50' } query_string = parse.urlencode(query_string_dict) url = 'http://www.baidu.com/s?{}'.format(query_string) print(url)
#1、字符串相加 baseur1 = 'http://www.baidu.com/s?' params = 'wd=%E7xxxx&pn=20' url = baseur1+params #2、字符串格式化(占位符) params ='wd=%E7xxxx&pn=20' url = 'http://www.baidu.com/s?%s'%params #3、format()方法 url = 'http://www.baidu.com/s?{}' params ='wd=%E7xxxx&pn=20' url = url.format(params)
from urllib import request from urllib import parse def get_url(word): baseurl = 'http://www.baidu.com/s?' params = parse.urlencode({'wd':word}) url = baseurl + params return url def request_url(url,filename): headers = {'User-Agent': 'Mozilla/5.0'} req = request.Request(url, headers=headers) res = request.urlopen(req) html = res.read().decode('utf-8') # 保存到本地文件 with open(filename, 'w') as f: f.write(html) if __name__ == '__main__': word = input('请输入搜索内容:') url = get_url(word) filename = '{}.html'.format(word) request_url(url,filename)
from urllib import parse string = '美女' print(parse.quote(string)) # 结果: %E7%BE%8E%E5%A5%B3
改写之前urlencode()代码,使用quote()方法实现
from urllib import parse url = 'http://www.baidu.com/s?wd={}' word = input('请输入要搜索的内容:') query_string = parse.quote(word) print(url.format(query_string))
from urllib import parse string = '%E7%BE%8E%E5%A5%B3' result = parse.unquote(string) print(result)
#1.urllib.request 1,req=urllib.request.Request(url,headers) # 请求对象 2.res= urllib.request.urlopen(req) # 二字节的形式打开文件 3.html=res.read().decode('utf-8') # 读取文件 并转换为字符串 #2.res方法 res.read() # 读取网络 res.getcode() # 返回响应码 res.geturl() # 重定向 #3.urllib.parse
#编码
urllib.parse.urlencode({}) urllib.parse.quote(srting)
#解码 urllib.parse.unquote()
1、确定所抓取数据在响应中是否存在(右键 - 查看网页源码 - 搜索关键字) 2、数据存在: 查看URL地址规律 3、写正则表达式,来匹配数据 4、程序结构 1、使用随机User-Agent 2、每爬取1个页面后随机休眠一段时间
# 程序结构 class xxxSpider(object): def __init__(self): # 定义常用变量,url,headers及计数等 def get_html(self): # 获取响应内容函数,使用随机User-Agent def parse_html(self): # 使用正则表达式来解析页面,提取数据 def write_html(self): # 将提取的数据按要求保存,csv、MySQL数据库等 def main(self): # 主函数,用来控制整体逻辑 if __name__ == '__main__': # 程序开始运行时间戳 start = time.time() spider = xxxSpider() spider.main() # 程序运行结束时间戳 end = time.time() print('执行时间:%.2f' % (end-start))
1、输入贴吧名称 2、输入起始页 3、输入终止页 4、保存到本地文件:
例如:赵丽颖吧-第1页.html、赵丽颖吧-第2页.html ...
实现步骤:
(1)查看是否为静态页面
通过右键-查看网页源代码-搜索数据关键字
(2)找url规律
第一页:http://tieba.baidu.com/f?kw=??&pn=0
第二页:http://tieba.baidu.com/f?kw=??&pn=0
第n页: http://tieba.baidu.com/f?kw=??&pn=(n-1)*50
(3)获取网页内容
(4)保存(本地文件,数据库)
(5)实现代码
from urllib import request from urllib import parse import time import random from useragents import ua_list class BaiduSpider(object): def __init__(self): self.url = 'http://tieba.baidu.com/f?kw={}&pn={}' # 获取相应内容 def get_html(self, url): headers = { 'User-Agent': random.choice(ua_list) } req = request.Request(url=url, headers=headers) res = request.urlopen(req) html = res.read().decode('utf-8') print(headers) return html # 解析相应内容(提取所需数据) def parse_html(self): pass # 保存 def write_html(self, filename, html): with open(filename, 'w', encoding='utf-8') as f: f.write(html) # 主函数 def main(self): # 拼接每一页的地址 # 接收用户的输入(例如贴吧名,第几页) name = input('请输入贴吧名:') begin = int(input('请输入起始页')) end = int(input('请输入终止页')) # url 缺两个数据: 贴名吧 pn params = parse.quote(name) for page in range(begin, end + 1): pn = (page - 1) * 50 url = self.url.format(params, pn) filename = '{}-第{}页.html'.format(name, page) # 调用类内函数 html = self.get_html(url) self.write_html(filename, html) # 每爬取1个页面随机休眠1-3秒钟 time.sleep(random.randint(1, 3)) print('第%d页爬取完成' % page) if __name__ == '__main__': start = time.time() spider = BaiduSpider() spider.main() end = time.time() print('执行时间:%.2f' % (end-start))
ua_list = [ 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50', 'Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.9.168 Version/11.50', ]
创建正则编译对象
pattern = re.compile(r'正则表达式',re.S)
r_list = pattern.findall(html)
元字符 | 含义 |
---|---|
. | 任意一个字符(不包括\n) |
\d | 一个数字 |
\s | 空白字符 |
\S | 非空白字符 |
[] | 包含[]内容 |
* | 出现0次或多次 |
+ | 出现1次或多次 |
import re # 方法一 pattern = re.compile('.',re.s) # 方法二 pattern = re.compile('[\s\S]')
1、在整个表达式匹配成功的前提下,尽可能多的匹配 *
2、表示方式:.* .+ .?
1、在整个表达式匹配成功的前提下,尽可能少的匹配 *
2、表示方式:.*? .+? .??
示例:
import re html = ''' <html> <div><p>九霄龙吟惊天变</p></div> <div><p>风云际会浅水游</p></div> </html> ''' # 贪婪匹配 pattern = re.compile('<div><p>.*</p></div>',re.S) r_list = pattern.findall(html) #['<div><p>九霄龙吟惊天变</p></div>\n <div><p>风云际会浅水游</p>']
# 非贪婪匹配 pattern = re.compile('<div><p>.*?</p></div>',re.S) r_list = pattern.findall(html) print(r_list)
#['<div><p>九霄龙吟惊天变</p>', '<div><p>风云际会浅水游</p>']
pattern = re.compile('<div><p>(.*?)</p></div>',re.S)
r_list = pattern.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个及以上分组(),则结果中以元组形式显示 [(),(),()]
练习1:页面结构如下
<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>
从以上html代码结构中完成如下内容信息的提取:
问题1 :[('Tiger',' Two...'),('Rabbit','Small..')] 问题2 : 动物名称 :Tiger 动物描述 :Two tigers two tigers run fast ********************************************** 动物名称 :Rabbit 动物描述 :Small white rabbit white and white
# r = r'title="(.*?)".*?class="content">\s+(.*?)\s+</p>' r = r'<div class="animal">.*?<a title="(.*?)".*?<p class="content">\s+(.*?)\s+</p>' pattern = re.compile(r, re.S) r_list = pattern.findall(html) print(r_list) #遍历方法1 for i, j in r_list: print('动物名称:',i,'动物描述:',j) print("*"*20) #遍历方法2 for r in r_list: print('动物名称:',r[0].strip()) print('动物描述:',r[1].strip()) print("*" * 20)
练习2:爬取猫眼电影信息 :猫眼电影-榜单-top100榜
第1步完成: 猫眼电影-第1页.html 猫眼电影-第2页.html ... ... 第2步完成: 1、提取数据 :电影名称、主演、上映时间 2、先打印输出,然后再写入到本地文件
from urllib import request, parse import re import time import random from useragents import ua_list class MaoyanSpider(object): def __init__(self): self.url = 'https://maoyan.com/board/4?offset={}' #计数 self.num=0 def get_html(self, url): headers = { 'User-Agent': random.choice(ua_list) } req = request.Request(url=url, headers=headers) res = request.urlopen(req) html = res.read().decode('utf-8') #直接调用解析函数 self.parse_html(html) def parse_html(self,html): #创建正则的编译对象 re_='<div class="movie-item-info">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p> ' pattern = re.compile(re_,re.S) #film_list:[('霸王别姬','张国荣','1993')] film_list = pattern.findall(html) self.write_html(film_list) def write_html(self,film_list): film_dict={} for film in film_list: film_dict['name']=film[0].strip() film_dict['star']=film[1].strip() film_dict['time']=film[2].strip()[5:15] print(film_dict) self.num += 1 def main(self): for offset in range(0, 91, 10): url = self.url.format(offset) self.get_html(url) time.sleep(random.randint(1,2)) print('共抓取数据',self.num,"部") if __name__ == '__main__': start = time.time() spider = MaoyanSpider() spider.main() end = time.time() print('执行时间:%.2f' % (end - start))
ua_list = [ 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0', 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 QQBrowser/6.9.11079.201', 'Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.9.168 Version/11.50', ]