爬虫再体验--爬取当当书籍--进阶篇
来源:互联网 发布:matlab 字符串数组 编辑:程序博客网 时间:2024/05/27 01:20
爬取当当书籍–进阶篇
在上一篇爬虫初体验中, 叙述了我的小爬虫的整体构架以及中心思想,并且在小伙伴的反馈下,进行了改良,加入了许多的注释,方便大家学习与交流。
在基础篇中,只是简单的爬取了3个字段,小伪装了一下浏览器,并写入到了文件当中,是不是感觉有些low档次呢?
那么今天,咱们就来搞一些高大上的东西,
1、咱们会把爬虫伪装的更像一个浏览器,并不断随机更换User-Agent,正所谓爬虫伪装术+影分身,迷惑网管(网站管理员)。2、利用Chrome自带的抓包工具捕捉AJAX异步请求的JSON数据(网页源代码中不存在或者说找不到的数据)3、最后咱们把提取到的数据存到MySql数据库里
温馨提示:
本篇重点讲解以上三点,若有对细节和别的地方产生困惑的话,请阅读博主上一篇爬虫初体验--爬取当当书籍
爬虫的基本结构与上一篇文章一样,咱们再来回顾一下
分为5个文件:分别是1、spider_main.py 即主函数,用于启动爬虫 2、url_manager.py 这个是url管理器,用于管理url 3、html_downloader.py 这个是html下载器,用于下载给定url 的源码 4、html_parser.py 这个是html解析器,用于提取你想要的信息 5、html_outputer.py 这个是html输出器,用于把提取的信息存储到文件或者数据库
咱们今天的重点,是分别在html下载器、html解析器、html输出器上做文章。
主函数以及url管理器基本没变化,源码我会在文章末尾打包给各位
好了,开始进入正题
一、爬虫伪装术+影分身
有时候,我们的小爬虫爬着爬着就被网管拒之门外(403状态码拒绝访问),甚至刚开始就已经结束了。
那可怎么办呢,别着急,咱们来学两招
咱们在html下载器(html_downloader.py)里做文章
上一次咱们简单的加入了User-Agent,这次呢,咱们多加入几个字段
headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Accept-Encoding':'gzip, deflate, sdch, br', 'Accept-Language':'zh-CN,zh;q=0.8', 'Connection':'keep-alive', 'Host':'product.dangdang.com', 'Cookie':'', }
话说是这样,但是这些字段去哪里找呢?
来,咱们一起来找找,
首先打开浏览器,我用的Chrome,找到一个书籍网页,打开后按F12,出现调试窗口,点击菜单栏中的Network,然后点击下方的All
却发现什么也没有,不要紧,点击F5刷新网页,之后出来一大堆东西,随便点击一个(尽量选择含有较多Headers的),右方的Headers栏中会出来一大堆东西,Ruquests Headers中的东西,就是我们想要的
这些都是浏览器的标识,就相当于浏览器的身份证一样
想要了解每个字段的含义的话,推荐去看Requests Headers
到这里,咱们的伪装术完成了。
——————————————这是一个搞事的分割线—————————————–
单伪装成一个浏览器,可能对某些网站还是不太好使,那咱们就来个影分身
咱们随机换取User-Agent字段,让网管误以为每次访问都是不同的浏览器
首先用一个list列表来存放多个User-Agent,然后再利用random模块的 choice方法,随机抽取一个User-Agent,放进headers里去,代码如下:
# coding: utf-8import requests,randomclass HtmlDownloader(object): """docstring for HtmlDownloader""" def __init__(self): #用来存放User-Agent的list self.user_agent_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", ] def download(self,url): UA = random.choice(self.user_agent_list) if url == None: return headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Accept-Encoding':'gzip, deflate, sdch, br', 'Accept-Language':'zh-CN,zh;q=0.8', 'Connection':'keep-alive', 'Host':'product.dangdang.com', 'Cookie':'', } # print "headers:%s"%(headers) res = requests.get(url,headers=headers,timeout=20) html = res.text return html
以上只是放入了几个User-Agent而已,我个人又整理了300多个,需要的话,拿去玩!
300多个User-Agent
好了,影分身+伪装术到此结束,是不是有点意思呢!
二、捕捉AJAX异步请求的JSON数据
有时候我们虽然拿到了整个页面的html源代码,但是怎么也找不到咱们想要拿的数据,是不是好奇怪。
这是因为那些数据是用AJAX加载出来的,如果不懂什么是AJAX,推荐看百度百科AJAX
这次咱们依然用到调试者模式,不过这次咱们的目的更加明确,就是拿通过AJAX异步请求才拿到的数据(源代码里找不到的),这次咱们拿的是总评论数,好评论数,中评,差评以及好评率(之前的做法是拿不到的,不信你试试)
还是Network,点击下方的 XHR,点击F5,然后一个一个找,右边的Preview会显示data内容,最终咱们找到了咱们想要的那个AJAX请求
之后咱们找到他所对应的url,还是在Headers里边
这个就是获取这个数据的url,你可以复制到地址栏里访问一下,返回的数据是json格式,不懂json的同学,可以百度一下。
url拿到了,咱们也就离成功不远了,
为了方便使用,我在url_parser.py里边又定义了一个方法,用来进行AJAX请求并放回的下载json数据。
http://product.dangdang.com/index.php?r=comment%2Flist&productId=25120084&categoryPath=01.01.02.00.00.00&mainProductId=25120084&mediumId=0&pageIndex=1&sortType=1&filterType=1&isSystem=1&tagId=0&tagFilterCount=0
只拿回来这个url还不够,咱们还需要对它进行分析,因为有些参数是随着不同的书籍是变的,我把多个同类的url做对比,发现其中的“productId”、“categoryPath”、“mainProductId”是变化的。
于是我们在书籍的源码中拿到这三个字段然后进行拼接。形成每本书籍特有的AJAX请求
代码如下:
# coding: utf-8from bs4 import BeautifulSoupimport re,json,requestsclass HtmlParser(object): """docstring for HtmlParser""" def get_new_urls(self,page_url,soup): new_urls = set() links = soup.find_all('a',href=re.compile(r"http://category.dangdang.com/[.\w]+html|http://product.dangdang.com/[.\w]+html")) for link in links: new_url = link['href'] new_urls.add(new_url) return new_urls def get_new_data(self,page_url,html,soup): data = {} link_node = soup.find('div',class_='name_info') if link_node != None: data['url'] = page_url h1_node = link_node.find('h1') data['title'] = h1_node.get_text().strip() #书名 price_node = soup.find('p',id='dd-price') data['price'] = price_node.get_text().strip()[1:] #价钱 #用正则表达式拿取 ma = re.search(r'"productId":"[\d]+"',html) productId = eval(ma.group().split(':')[-1]) ma = re.search(r'"categoryPath":"[\d.]+"',html) categoryPath = eval(ma.group().split(':')[-1]) # print page_url ma = re.search(r'"mainProductId":"[\d.]+"',html) mainProductId = eval(ma.group().split(':')[-1]) #对Ajax的url进行拼接 json_url = 'http://product.dangdang.com/index.php?r=comment%2Flist&productId={productId}&categoryPath={categoryPath}&mainProductId={mainProductId}&mediumId=0&pageIndex=1&sortType=1&filterType=1&isSystem=1&tagId=0&tagFilterCount=0'.format(productId=productId,categoryPath=categoryPath,mainProductId=mainProductId) #调用方法,下载下来json数据 json_html = json.loads(self.getJsonText(json_url)) summary = json_html['data']['summary'] data['all_comment_num'] = summary['total_comment_num'] #总评论数 data['good_comment_num'] = summary['total_crazy_count'] #好评数 data['middle_comment_num'] = summary['total_indifferent_count'] #中评数 data['bad_comment_num'] = summary['total_detest_count'] #差评数 data['good_rate'] = summary['goodRate'] #好评率 return data #用于加载请求AJAX并获取json数据的方法 def getJsonText(self,url): try: r = requests.get(url, timeout = 30) r.raise_for_status() r.encoding = r.apparent_encoding return r.text except: print '获取失败' return '' def parse(self,page_url,html): if page_url == None: return soup = BeautifulSoup(html,'html.parser') new_urls = self.get_new_urls(page_url,soup) new_data = self.get_new_data(page_url,html,soup) return new_urls,new_data
以上用到了正则表达式(Python正则教程)
还有Python内置json库(Python的json教程)
这一部分就算完成了,有什么不懂的,欢迎骚扰博主
三、存储到数据库
Python连接到数据库比java等其他语言简单多了,只需要简单的几句话
推荐教程Python对数据库操作教程
(偷个懒,写了这么多,太累了,大家去看我给你们推荐的教程吧,嘿嘿)
下面附上代码,其中有文件写入部分,和提交到数据库部分:
# coding: utf-8import sys,MySQLdbreload(sys)sys.setdefaultencoding('utf-8')class HtmlOutputer(object): """docstring for Outputer""" def __init__(self): self.datas = [] def collect(self,data): if data == None or len(data) == 0: return self.datas.append(data) #这个依然是输出到html文件里 def output_html(self): f = open('out.html','w') try: f.write('<html>') f.write('<body>') f.write('<table>') for data in self.datas: f.write('<tr>') f.write('<td>%s</td><td>%s</td><td>%s</td>'%(data['url'],data['title'].encode('utf-8'),data['price'].encode('utf-8'))) f.write('<td>%s</td>'%(data['all_comment_num'])) f.write('<td>%s</td>'%(data['good_comment_num'])) f.write('<td>%s</td>'%(data['middle_comment_num'])) f.write('<td>%s</td>'%(data['bad_comment_num'])) f.write('<td>%s</td>'%(data['good_rate'])) f.write('</tr>') f.write('</table>') f.write('</body>') f.write('</html>') except: print '输出失败!' finally: f.close() #这个才是今天的重点,存储到数据库里 def output_mysql(self): # print 'mysql' conn = MySQLdb.Connect( host = '127.0.0.1', port = 3306, user = 'root', passwd = '123', db = 'python_1', charset = 'utf8' ) cursor = conn.cursor() print 'datas:%d'%(len(self.datas)) for data in self.datas: try: # print data # print len(data) sql = "INSERT INTO book(url,title,price,all_comment_num,good_comment_num,middle_comment_num,bad_comment_num,good_rate) VALUES('%s','%s','%s','%s','%s','%s','%s','%s')"%(data['url'],data['title'].encode('utf-8'),data['price'].encode('utf-8'), data['all_comment_num'],data['good_comment_num'],data['middle_comment_num'],data['bad_comment_num'],data['good_rate'] ) cursor.execute(sql) # print 'inserting' conn.commit() print 'insert success' except: print '插入失败!' continue print 'insert end!' cursor.close() conn.close()
代码调试ok,有图有真相
以上就是所有的内容,篇幅有限,有些地方说的不够详细还请各位谅解,有什么不明白的,或者什么我写的不好的,需要改良的话,可以和博主交流交流嘛,邮箱:1131726190@qq.com
下一篇,博主将简单介绍爬虫利器—Scrapy框架,并爬取大众点评网的美食店铺,有兴趣的同学,记得关注博主呦!
源码奉上:http://pan.baidu.com/s/1qYymLlU 拿去玩!
温情提示:
由于各个网站一直在维护与更新,爬取规则会有时效性,所以代码仅供参考!
拯救不开心!!!
- 爬虫再体验--爬取当当书籍--进阶篇
- 爬虫初体验-- 爬取当当书籍
- 爬虫实战一:爬取当当网所有 Python 书籍
- Python 爬虫第三步 -- 多线程爬虫爬取当当网书籍信息
- 利用Python的requests模块爬取当当网的书籍信息(待续)
- 【Python爬虫第二弹】基于爬虫爬取豆瓣书籍的书籍信息查询
- python爬虫爬取豆瓣书籍信息并生成表格
- 爬虫系列4:scrapy技术进阶之多页面爬取
- python爬虫进阶(一):静态网页爬取
- python爬虫进阶(二):动态网页爬取
- 爬取当当网图书图片
- 爬取当当网图书信息
- Python爬取当当网亚马逊书名
- 当当网爬虫
- 零基础掌握百度地图兴趣点获取POI爬虫(python语言爬取)(进阶篇)
- python爬虫:爬取豆瓣读书某个tag下的书籍并存入excel
- Python爬取当当、京东、亚马逊图书信息
- 简单Python3爬虫程序(5)进阶:知乎网的登录与用户相关信息爬取
- 排序算法-插入排序之直接插入
- HBuilder mui 手势事件
- eclipse点击一个变量使相同名称变量高亮显示的方法
- 百度:度度熊想去商场买一顶帽子,买第三便宜的帽子
- 伯努利分布期望,方差推导
- 爬虫再体验--爬取当当书籍--进阶篇
- 程序开发 基本知识体系
- GooFlow .net 工作流程图 jquery 流程 mvc
- Android7.0的相机相册遇到问题
- 1591-交叉排序
- 密码学 现代加密方式基础
- CentOS7.3 安装及配置MySql5.7
- NYOJ 518-取球游戏
- 270