├── ZhiHu ├── GetReplies.py ├── GetFocus.py ├── GetImgs.py ├── FirstLogin.py ├── GetImgs-Thread.py └── GetQuestionTopic.py ├── LaGou ├── 拉钩.md └── lagouCrawler.py ├── ReadMe-Baidupic.md ├── ReadMe-Lagou.md ├── ReadMe-Luowang.md ├── ReadMe-Zhihu.md ├── ReadMe-Sujin.md ├── README.md ├── ReadMe-One.md ├── SuJin └── sujin.py ├── XiciDaili └── Proxy.py ├── Douban └── DoubanBook.py ├── BaiDu └── GetPicture.py ├── README-Douban.md ├── One └── one.py └── LuoWang └── GetSong.py /ZhiHu/GetReplies.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LaGou/拉钩.md: -------------------------------------------------------------------------------- 1 | ### 一. 职位数量分布 2 | 3 | ### 二. 城市提供职位数量 -------------------------------------------------------------------------------- /ReadMe-Baidupic.md: -------------------------------------------------------------------------------- 1 | ##py2.7 根据关键字从百度下载图片,并且能够根据需要过滤图片。 2 | 3 | * [py3.0作者博客](http://lovenight.github.io) **Thank you** 4 | * 最近做App需要图片资源,就想写个爬虫爬,想起来以前收藏的py3.0版本,改成2.7版本。 5 | * 改动之后下载图片的数量可以**自己控制**,下载之后的图片可**根据尺寸保留**。 6 | -------------------------------------------------------------------------------- /ReadMe-Lagou.md: -------------------------------------------------------------------------------- 1 | ### 思路 2 | 3 | * 开始即根据职位来抓取相应的招聘职位数量。但是只有 1w 多。数量不达标。 4 | * 组合关键字抓取。如组合城市,职位,服务领域。即可达成抓取 5w 数量目标。 5 | 6 | ### 细节 7 | 8 | 如下 Request URL 中的 %E7... 即为对应查询关键字的 16 进制表现形式。 9 | 10 | ![](http://ww1.sinaimg.cn/large/b10d1ea5gw1f6un014yqij20j7033wet.jpg) 11 | 12 | 如下 Form Data 皆为 Post 需要构建的表单。 13 | 14 | ![](http://ww2.sinaimg.cn/large/b10d1ea5gw1f6un0gaislj20ja04ldfz.jpg) 15 | 16 | ### 存储 17 | 18 | * 为保证存储和查询速度。选择 MongoDB 数据库。 19 | * 为保证插入不重复。对插入语句进行优化。 20 | 21 | ```python 22 | def insert_data(self,data): 23 | data['_id'] = data['positionId'] 24 | data['updateTime'] = datetime.datetime.now() 25 | # 防止重复插入 26 | db.Collection.update_one( 27 | filter={'_id': data['_id']}, 28 | update={'$set': data}, 29 | upsert=True 30 | ) 31 | count = db.Collection.count() 32 | print u'已经存储了:',count,u'条记录' 33 | ``` -------------------------------------------------------------------------------- /ReadMe-Luowang.md: -------------------------------------------------------------------------------- 1 | ## [落网] 2 | 3 | * 落网的音乐逼格真的很高,都是自己喜欢的民谣类型,故爬虫抓取之。强烈推荐大家去欣赏落网的音乐,如果有兴趣也可以知乎一下落网的历史。真的非常不错。 4 | 5 | ### 说明 6 | * 整个网站还是很简单的,没有模拟登陆,验证码啥的,甚至请求头都不用写。爬虫也没有写的太暴力,"温柔"的爬取,为此加了延时,另一方面也是怕被识别出是机器。 7 | 8 | ### 准备 9 | * 工具:python 2.7,PyCharm 10 | * 类库:Requests,BeautifulSoup,os 11 | 12 | ### 分析 13 | * 进入落网官网,发现如下图所示,其中804(可以自己输入相应期刊)就是期刊,每一期都有十左右首音乐。 14 | 15 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/%E6%AF%8F%E4%B8%80%E6%9C%9F%E6%AD%8C%E5%8D%95.jpg) 16 | 17 | * 点击页面进入HTML元素页面,可以看见歌曲名字,专辑名,歌者,但是怎么没有歌曲资源的url? 18 | 19 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/%E6%AD%8C%E6%9B%B2%E5%90%8D%E5%AD%97.jpg) 20 | 21 | * 没有歌曲的url我们就不能下载,那么我们要看看我们每一次换歌曲,发生了什么请求,点击审查元素,如下结果。 22 | 23 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/%E6%AD%8C%E6%9B%B2url.jpg) 24 | 25 | * 如上图所示一个Request url,我们复制进浏览器地址输入栏,发现如下所示,没错这就是我们下载的url了。 26 | 27 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/%E6%AD%8C%E6%9B%B2.jpg) 28 | 29 | ### 开始 30 | 31 | * 开始之前我们要获取歌曲相应的信息,我们采用bs这个库来解析HTML元素。 32 | * 我们采用Requests来进行网络请求与下载,下载音乐就要用到稍微逼格高一点的用法了,我们下载它的响应流文件。 33 | * 具体用法见[Requests响应体内容工作流](http://cn.python-requests.org/en/latest/user/advanced.html) 34 | 35 | ### 总结 36 | * 抓取还是比较简单的,只用到了基本的requests和beautifulsoup,只是很难想到要流式读取响应头,这是requests的高级用法。 37 | * 我们抓取的目的就是把歌曲下载到本地,自由不用联网的听,速度个人感觉不要太追求,不要坑人家服务器啊。 38 | 39 | ### 参考 40 | 41 | * [落网爬虫](https://github.com/shuson/luooder.git) 42 | 43 | ### 结果 44 | 45 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/%E7%BB%93%E6%9E%9C.jpg) 46 | 47 | ### 感谢 48 | 49 | * [TZS](https://github.com/1041218129) 50 | * [DLX4](https://github.com/DLX4) 51 | -------------------------------------------------------------------------------- /ReadMe-Zhihu.md: -------------------------------------------------------------------------------- 1 | ## 知乎爬虫 2 | 3 | #####一:firstLogin模拟登录知乎--代码解读思路整理。 4 | * 很多网站需要登录才能访问更多的内容,so登录必不可少,这部分代码也是我fork的源码的author.py(在第二部分可见,尊重作者,感谢:) 5 | * 首先要想模拟登录,浏览器的工作原理一定要了解一点,推荐Chorme浏览器自带的审查元素(右键即可)然后观察Network的变化 6 | * 模拟登录首先要构建Header请求头,这是让请求网站认为你是浏览器(not spider) 7 | * 第二步要获取xsrf--详细请看http://baike.baidu.com/link?url=x5M7ywYtlBL5LDGsZ61VTJHzS582Y1uZnXgor_LszDF0k7-BY7RA8YwkJ02yxxk-hAANTw32wcHGRRl3Xqi2-a 我们要做的就是利用re或者bs去相应的网站获得这个参数,以证明我们不是坏银== 8 | * 正确输入账号密码之后,肯定要输入验证码啦==简单的思路就是把验证码下载下来,然后人眼识别,手动填入(当然大神的源码中,能够直接破解,自动填写,但是在我的win7上好像不可以==)所以我稍微改了一下,用网页自动打开== 9 | * 首次登陆成功之后,以后肯定不想重复再做这些啦,所以有cookie的存在啊~~注意一点:firstLogin代码执行并不是一开始就执行main函数--会先检查cookie== 10 | * 在firstLogin代码里面有详细的步骤(删除了一些原来大神的完善代码),只是为了自己更好理解一下,思路更加清晰一些(尊重产权==) 11 | 12 | #####二:getImgs爬取知乎问题下的图片--女生拥有短发是怎样一种体验? 13 | * 这一小段代码作用是,从知乎网上down图片,之前看到一个帖子--女生短发是一种什么样的体验?看到里面好多美女图片,就想着全部下载下来== 14 | * 登录的代码比较简单(大都大同小异),引用的是这位大牛写的author.py模块--https://github.com/wuchangfeng/zhihu-python--自己也在学习他的源码,down在工程里,索性直接引用了。十分欣赏。 15 | * 遇到网页需要加载更多时候,思路我看了http://lovenight.github.io这位大牛的,后来发现1中的代码也有这一部分的处理,我就自己对着浏览器的请求以及1中大神的代码,写出来了。 16 | * 之所以没有pull到1中大神project中,因为自己代码太渣。总之,会继续加上新的想法的。加油。 17 | 18 | ####三:getQuestion_topic爬取某个话题下面的所有问题。 19 | * 输入“生活”这个话题时,浏览器返回http://www.zhihu.com/topic/19551147这个url,其中19551147就是这个话题的link-id。 20 | * 上面那个link-id 很重要http://www.zhihu.com/topic/******/questions?page=# 。用link-id替换这个*****,用数字(1,2,3....)替换# 21 | * 代码已写,思路以上。 22 | * 可以文件存储或者数据库存储。 23 | * 后面的自己不想写了,感觉没啥意思,准备爬全局问题,监测一天的问题导向。 24 | 25 | ####四:**getImgs**爬取知乎问题下的图片--女生拥有短发是怎样一种体验? 26 | * 加上了Thread,Quene跑的快飞起了--五六分钟爬了1000条左右 27 | * 加上的异常处理,碰到了不能下载下来的,直接跳过去 28 | * 线程的知识参考[简书作者](http://www.jianshu.com/p/544d406e0875)**感谢** 29 | 30 | ####五:要干嘛?还没想好== 31 | * 所有话题下面的问题http://www.zhihu.com/topic/19776751/top-answers 32 | 33 | ####六:getFocus批量关注某个话题下面的用户 34 | * 详细请见代码 35 | * 有点疑问 批量获得关注者 那个start参数有什么规律== 36 | * 更新了最新的代码,start是十位数的时间戳 37 | -------------------------------------------------------------------------------- /ReadMe-Sujin.md: -------------------------------------------------------------------------------- 1 | 抓取素锦网站上文章 2 | 3 | ## 1. 步骤分析 4 | 5 | 如下两张图片既是网站主要布局结构,我们要抓取的数据也是一目了然。 6 | 7 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/Sujin1.png) 8 | 9 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/sunjin2.png) 10 | 11 | 拖动页面。可以看到加载更多按钮。但是地址栏没有变化。点击 F12 调试模式。如 2 所示看见 http//isujin.com/page/3。容易猜想输入 n(n 正常合理范围之内) 也是可以获得数据的。并且如果返回数据成功,返回 statusCode == 200。这个状态码对我们非常有用。 12 | 13 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/sujin3.png) 14 | 15 | 点击页面,查看源码,红框内即为要爬取的字段内容。 16 | 17 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/sujin4.png) 18 | 19 | ## 2. 抓取代码 20 | 21 | 抓取的核心代码如下所示,数据的过滤收集采用的 BeautifulSoup4 这个库。很简单。 22 | 23 | ```python 24 | def getPage(self, pageIndex): 25 | url = 'http://isujin.com/page/' + str(pageIndex) 26 | req = requests.get(url, headers=self.headers) 27 | return req 28 | 29 | def getItem(self,pageIndex): 30 | req = self.getPage(pageIndex) 31 | if req.status_code == 200: 32 | print '第',pageIndex,'页ok' 33 | soup = BeautifulSoup(req.content, 'html.parser') 34 | for link in soup.find_all('div', 'post'): 35 | sjContent = Content() 36 | sjContent.set("title", link.a['title']).save() 37 | sjContent.set("detail", self.getContent(link.a['href'])).save() 38 | sjContent.set("ids", link.a['data-id']).save() 39 | sjContent.set("img", link.a.contents[1]['src']).save() 40 | sjContent.set("intr", soup.find_all('p')[1].string).save() 41 | 42 | def getContent(self,contentUrl): 43 | string = '' 44 | reqContent = requests.get(contentUrl) 45 | contentSoup = BeautifulSoup(reqContent.content, 'html.parser') 46 | for s in contentSoup.find('div', 'content').p.stripped_strings: 47 | string = string + s + '\n' 48 | return string 49 | 50 | def start(self): 51 | nowPage = 1 52 | while nowPage <= 10: 53 | nowPage += 1 54 | time.sleep(4) 55 | self.getItem(nowPage) 56 | ``` 57 | ## 3. 存储代码 58 | 59 | 存储过程很简单。存储在 leancloud 云端。用 leanCloud 官方提供的 python sdk 来进行数据的存储工作。部分代码如下: 60 | 61 | ```python 62 | @property 63 | def title(self): 64 | return self.get('title') 65 | @title.setter 66 | def title(self, value): 67 | return self.set('title', value) 68 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 关于 2 | 3 | * 学习 Python 时写一些简单的爬虫来获取需要的数据。 4 | * 有些程序估计写的比较早,一些网站的验证机制估计也变了,只做参考用。 5 | * 不定期更新。欢迎 PR。 6 | 7 | ## 爬虫实例 8 | 9 | * Readme_Luowang:关于如何爬取落网音乐,下载到本地的小程序。 10 | * Readme_Baidu:关于如何基于 Py2.7 根据关键词从百度下载图片的小程序。 11 | * Readme_Zhihu:关于如何抓取知乎上一些信息的程序。 12 | * Readme_One:关于如何爬取 One 网站上的每日一图以及 One 问答,并且存储在 LeanCloud 云后台。 13 | * Readme_Sujin:关于如何爬取素锦网站上的好文章,并且存储在 LeanCloud 云后台。 14 | * Readme_Douban:关于如何爬取豆瓣图书 Top250。 15 | * Readme_Lagou:关于如何从拉勾网爬取较大量的职位信息以及存储至 NoSql 类型数据库中。 16 | * Readme_XiciDaili:抄自知乎一个回答。改成 MongoDB 存储以及加了验证机制。但是可用性不是很高,大概30%。 17 | 18 | 19 | ## 爬虫基础 20 | 21 | * [Scrapy 爬取豆瓣信息并用 MongoDB 存储](http://1992mrwang.blog.51cto.com/3265935/1583539) 22 | * [Scrapy 爬取知乎用户数据并用 MySQL 存储](http://python.jobbole.com/85125/) 23 | * [Python 爬虫技巧总结](http://www.codeceo.com/article/python-spider-skills.html#0-tsina-1-54529-397232819ff9a47a7b7e80a40613cfe1) 24 | * [Python 爬虫学习系列教程](http://cuiqingcai.com/1052.html) 25 | * [Python 工程师博客](http://zhuanlan.zhihu.com/xlz-d) 26 | 27 | ## 爬虫进阶 28 | 29 | * [Requests 库](http://cn.python-requests.org/zh_CN/latest/user/quickstart.html) 30 | * [BeautifulSoup 库](http://beautifulsoup.readthedocs.io/zh_CN/latest/) 31 | * [Scrapy 入门文档](http://scrapy-chs.readthedocs.org/zh_CN/0.24/intro/tutorial.html) 32 | * [Scrapy 初级实战](http://www.ituring.com.cn/article/114408) 33 | * [Scrapy 初级实战之数据可视化](http://aljun.me/post/9) 34 | * [Python 抓取微信公众号文章](http://mp.weixin.qq.com/s?__biz=MzI0NjIxMzE5OQ==&mid=2656697797&idx=1&sn=a8e93bbc960c7564c2054a24e2414145#rd) 35 | * [Python 模拟登陆知乎(8/7日更)](http://blog.csdn.net/think_ycx/article/details/52104529) 36 | * [Python 与知乎相关的爬虫(8/21日更)](http://marcovaldong.github.io/2016/08/18/Python%E7%88%AC%E8%99%AB%E7%88%AC%E5%8F%96%E7%9F%A5%E4%B9%8E%E5%B0%8F%E7%BB%93/) 37 | * Python 爬取知乎专栏文章推送至 Kindle 38 | 39 | ## 数据分析 40 | 41 | * [用 pygal 来绘制SVG 显示数据](http://pygal.org/en/stable/documentation/types/line.html#time) 42 | * [百度的良心之作 ECharts](http://echarts.baidu.com/demo.html#pie-roseType) 43 | 44 | ## Python 相关 45 | 46 | * [Python2 中编码的问题](https://zhuanlan.zhihu.com/p/20612337?refer=xlz-d) 47 | 48 | ## 书籍推荐 49 | 50 | * 《用 Python 进行数据分析》 51 | * 《Python 数据挖掘入门与实战》 52 | * 《干净的数据-数据清洗与入门实践》 53 | * 《Python 网络数据采集》 54 | * 《集体智慧编程》 55 | * 《数据挖掘导论》 56 | 57 | ## 感谢 58 | 59 | * [suzumiyang](https://github.com/suzumiyang) 参与落网爬虫的改进 60 | 61 | -------------------------------------------------------------------------------- /ReadMe-One.md: -------------------------------------------------------------------------------- 1 | 爬取 One 上面的每日一图以及问答 2 | 3 | ## 一. 过程分析 4 | 5 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/one1.png) 6 | 7 | 如上即为 One 首页。挺简单的,但是我们要批量抓取数据。只是简单的首页展示并不能满足。看网页源码如下: 8 | 9 | ![](http://ww2.sinaimg.cn/large/b10d1ea5jw1f61mz739aaj20s3061q58.jpg) 10 | 11 | 如网页源码所标注。目标应该就是我们的链接 url。我们将其复制进地址栏。果然验证猜想。另外几个圈起来的即为要抓取的字段。分别对应着:图片的 URL 地址,作者,日期,一句话简介。 12 | 13 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/one3.png) 14 | 15 | 而当初我们就是要抓起批量的数据。所以这个 1406 就是我们首先要开刀。容易猜想出他就是历史内容。为验证,我们随机输入 1230 。果然如下所示。再多验证几次。大抵如是。所以我们抓取的时候,页面可以自己指定范围。当然会有 404 的时候。到时候我们也有解决的办法。 16 | 17 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/one4.png) 18 | 19 | One 的文章源码结构也是如上分析。道理亦然。 20 | 21 | ![](http://7xrl8j.com1.z0.glb.clouddn.com/one5.png) 22 | 23 | 24 | 25 | ## 二. 抓取代码 26 | 27 | ```python 28 | def getContent(self,url): 29 | req = requests.get(url,headers=self.headers) 30 | if req.status_code == 200: 31 | oneImgs = OneImg() 32 | # 图片 URL 33 | pattern0 = re.compile('
.*?.*?
',re.S) 34 | imgItem = re.findall(pattern0, req.content) 35 | oneImgs.set('imgUrl',imgItem[0]).save() 36 | # 简介描述 37 | pattern1 = re.compile('
(.*?)
',re.S) 38 | intrItem = re.findall(pattern1, req.content) 39 | oneImgs.set('imgIntr', intrItem[0]).save() 40 | # 作者 41 | pattern2 = re.compile('
(.*?)
',re.S) 42 | authItem = re.findall(pattern2, req.content) 43 | replaceBR = re.compile('
') 44 | authItem = re.sub(replaceBR, "\n", authItem[0]) 45 | oneImgs.set('imgAuth', authItem).save() 46 | # 日期 47 | pattern3 = re.compile('

(.*?)

.*?

(.*?)

',re.S) 48 | timeItem = re.findall(pattern3, req.content) 49 | oneImgs.set('imgDate', timeItem[0][0]+timeItem[0][1]).save() 50 | else: 51 | print '这一页失败了' 52 | ``` 53 | 54 | 核心代码如上。 55 | 56 | * 采用 request 库来进行网络请求。一旦返回的 statusCode 不等于 200,就不会进行下一步动作。 57 | * 采用正则来进行信息的过滤。当然 bs4 也是可以的。这里页面构造很简单,一会就写出来了。 58 | * 对于抓取的数字内容含有 "
",我们也可以采用正则过滤它,即用 "\n" 替代。 59 | 60 | ## 三. 数据存储 61 | 62 | ```python 63 | class OneImg(Object): 64 | # imgUrl 65 | @property 66 | def imgUrl(self): 67 | return self.get('imgUrl') 68 | @imgUrl.setter 69 | def imgUrl(self, value): 70 | return self.set('imgUrl', value) 71 | ``` 72 | 73 | * 数据的存储为 leanCloud 云存储。这一块可以自己看看官方文档,非常简单。 74 | 75 | 76 | * 如上代码。我们采用 property 属性来构建实体类。即面向对象思想的一些体现。这样存储过程就更为直观简单了。 -------------------------------------------------------------------------------- /SuJin/sujin.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from bs4 import BeautifulSoup 3 | import requests,re,time 4 | import leancloud 5 | from leancloud import Object 6 | 7 | 8 | ''' 9 | 爬虫类,用来从 Sujin 上抓取图片,文章,以及一些信息 10 | ''' 11 | class SuJin: 12 | def __init__(self): 13 | leancloud.init("xxxx", "xxxx") 14 | self.pageIndex = 1 15 | self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 16 | self.headers = {'User-Agent': self.user_agent} 17 | 18 | def getPage(self, pageIndex): 19 | url = 'http://isujin.com/page/' + str(pageIndex) 20 | req = requests.get(url, headers=self.headers) 21 | return req 22 | 23 | def getItem(self,pageIndex): 24 | req = self.getPage(pageIndex) 25 | if req.status_code == 200: 26 | print '第',pageIndex,'页ok' 27 | soup = BeautifulSoup(req.content, 'html.parser') 28 | for link in soup.find_all('div', 'post'): 29 | sjContent = Content() 30 | sjContent.set("title", link.a['title']).save() 31 | sjContent.set("detail", self.getContent(link.a['href'])).save() 32 | sjContent.set("ids", link.a['data-id']).save() 33 | sjContent.set("img", link.a.contents[1]['src']).save() 34 | sjContent.set("intr", soup.find_all('p')[1].string).save() 35 | 36 | def getContent(self,contentUrl): 37 | string = '' 38 | reqContent = requests.get(contentUrl) 39 | contentSoup = BeautifulSoup(reqContent.content, 'html.parser') 40 | for s in contentSoup.find('div', 'content').p.stripped_strings: 41 | string = string + s + '\n' 42 | return string 43 | 44 | def start(self): 45 | nowPage = 1 46 | while nowPage <= 10: 47 | nowPage += 1 48 | time.sleep(4) 49 | self.getItem(nowPage) 50 | 51 | ''' 52 | 实体类,用来将信息存储在 leancloud 53 | ''' 54 | class Content(Object): 55 | @property 56 | def title(self): 57 | return self.get('title') 58 | @title.setter 59 | def title(self, value): 60 | return self.set('title', value) 61 | 62 | @property 63 | def detail(self): 64 | return self.get('detail') 65 | @detail.setter 66 | def detail(self, value): 67 | return self.set('detail', value) 68 | 69 | @property 70 | def ids(self): 71 | return self.get('ids') 72 | @ids.setter 73 | def ids(self, value): 74 | return self.set('ids', value) 75 | 76 | @property 77 | def img(self): 78 | return self.get('img') 79 | @img.setter 80 | def img(self, value): 81 | return self.set('img', value) 82 | 83 | @property 84 | def intr(self): 85 | return self.get('intr') 86 | @intr.setter 87 | def intr(self, value): 88 | return self.set('intr', value) 89 | 90 | 91 | spider = SuJin() 92 | spider.start() 93 | -------------------------------------------------------------------------------- /XiciDaili/Proxy.py: -------------------------------------------------------------------------------- 1 | # -*- coding=utf-8 -*- 2 | __author__ = 'Rocky' 3 | import urllib2, time, datetime 4 | from lxml import etree 5 | import pymongo 6 | import sqlite3,time,requests 7 | 8 | class getProxy(): 9 | 10 | def __init__(self): 11 | self.user_agent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" 12 | self.header = {"User-Agent": self.user_agent} 13 | self.dbname="proxy.db" 14 | self.now = time.strftime("%Y-%m-%d") 15 | global collection 16 | global db 17 | client = pymongo.MongoClient("localhost", 27017) 18 | db = client.lagouDB 19 | 20 | def insertDB(self,data): 21 | db.IP.insert_one(data) 22 | print 'save ok' 23 | 24 | def getContent(self, num): 25 | nn_url = "http://www.xicidaili.com/nn/" + str(num) 26 | #国内高匿 27 | req = urllib2.Request(nn_url, headers=self.header) 28 | resp = urllib2.urlopen(req, timeout=10) 29 | content = resp.read() 30 | et = etree.HTML(content) 31 | result_even = et.xpath('//tr[@class=""]') 32 | result_odd = et.xpath('//tr[@class="odd"]') 33 | 34 | for i in result_even: 35 | t1 = i.xpath("./td/text()")[:2] 36 | p = "http"+":"+"//"+t1[0]+":"+t1[1] 37 | formatData = { 38 | "proxy": p 39 | } 40 | 41 | # 先验证是否有用 42 | if obj.test_useful(p): 43 | print p 44 | #obj.insertDB(formatData) 45 | ''' 46 | with open(path, 'a') as f: 47 | f.write(p + '\n') 48 | ''' 49 | 50 | for i in result_odd: 51 | t2 = i.xpath("./td/text()")[:2] 52 | p = "http" + ":" + "//" + t2[0] + ":" + t2[1] 53 | formatData = { 54 | "proxy": p 55 | } 56 | 57 | # 先验证是否有用 58 | if obj.test_useful(p): 59 | print p 60 | #obj.insertDB(formatData) 61 | 62 | 63 | def test_useful(self,proxy): 64 | try: 65 | proxies = {'http': proxy} 66 | print proxies 67 | r = requests.get('http://www.baidu.com',proxies=proxies) 68 | if r.status_code == 200: 69 | print 'Successfully get one' 70 | return True 71 | except Exception, e: 72 | print e 73 | return False 74 | 75 | def getProxies(self): 76 | print db.IP.count() 77 | cur = db.IP.find() 78 | for i in cur: 79 | print i 80 | 81 | 82 | def loop(self,page): 83 | for i in range(1,page): 84 | self.getContent(i) 85 | 86 | 87 | if __name__ == "__main__": 88 | path = 'mt_proxy.txt' 89 | now = datetime.datetime.now() 90 | print "Start at %s" % now 91 | obj=getProxy() 92 | obj.loop(5) 93 | #b = obj.getProxies() 94 | #obj.test_useful(b) 95 | 96 | 97 | -------------------------------------------------------------------------------- /ZhiHu/GetFocus.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'Wu_cf' 3 | from auth import islogin 4 | from auth import Logging 5 | import requests,cookielib,sys,urllib,urllib2,os,json,re,threading,Queue,time 6 | from bs4 import BeautifulSoup 7 | basePath = r'F:\Catch_IMGS' 8 | requests = requests.Session() 9 | requests.cookies = cookielib.LWPCookieJar('cookies') 10 | try: 11 | requests.cookies.load(ignore_discard=True) 12 | except: 13 | print u"尚未登录知乎==" 14 | if islogin() != True: 15 | print u"请重新登录==" 16 | #字符编码设置 17 | reload(sys) 18 | sys.setdefaultencoding('utf8') 19 | ''' 20 | 1:每一个用户都有一个hash_id应该是其用户标志,可以在chorme浏览器看得到这个,然后在html页面全局搜索即可用正则获得该值 21 | 2:可以在批量获得某个话题下面的用户,但是获得大量关注者那个 start参数还是没搞清楚什么意思 22 | 3:更换话题,假如想关注NBA底下的所有用户,需要首先获得nba这个话题的link-id 23 | 4:start关键字这么来的 t = int(time.time()) 十位数的时间戳 24 | ''' 25 | topic_url = 'http://www.zhihu.com/topic/19556945/followers' 26 | def getMessage(): 27 | r = requests.get(topic_url) 28 | raw_xsrf = re.findall('xsrf(.*)', r.text) 29 | _xsrf = raw_xsrf[0][9:-3] 30 | return _xsrf 31 | #得到hash_id 32 | def getHash(): 33 | global header_info 34 | hash_id_all = [] 35 | post_url = topic_url 36 | xsrf = getMessage() 37 | header_info = { 38 | "Accept":"*/*", 39 | "Accept-Encoding":"gzip,deflate,sdch", 40 | "Accept-Language":"zh-CN,zh;q=0.8", 41 | "Connection":"keep-alive", 42 | "Content-Length":"127", 43 | "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8", 44 | "DNT":"1", 45 | "Host":"www.zhihu.com", 46 | "Origin":"http://www.zhihu.com", 47 | "Referer":topic_url, 48 | "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", 49 | "X-Requested-With":"XMLHttpRequest", 50 | } 51 | for i in range(15): 52 | x = 0 + i * 20 53 | start = int(time.time()) 54 | payload={"offset":x, "start":start, "_xsrf":xsrf,} 55 | # time.sleep(3) 56 | result = requests.get(post_url, data=payload, headers=header_info) 57 | #print result.text 58 | raw_hash_id = re.findall('.*?', result.text) 59 | for item in raw_hash_id: 60 | if item[4:36] > 32: 61 | hash_id_all.append(item[4:36]) 62 | print "get hash_id",i,"success!" 63 | return hash_id_all 64 | def getFocus(): 65 | hash_id = getHash() 66 | xsrf = getMessage() 67 | i = 0 68 | for x in hash_id: 69 | i = i + 1 70 | params = json.dumps({"hash_id":x}) 71 | payload={"method":"follow_member", "params":params, "_xsrf":xsrf,} 72 | click_url = 'http://www.zhihu.com/node/MemberFollowBaseV2' 73 | try: 74 | result = requests.post(click_url, data=payload, headers=header_info) 75 | except Exception as e: 76 | print u"不能关注了" 77 | 78 | if result.status_code == 200: 79 | print u"关注成功"," ",i 80 | else: 81 | print u"fucking" 82 | print u"就这么多了!!!!" 83 | 84 | def main(): 85 | #getHash() 86 | getFocus() 87 | if __name__ == '__main__': 88 | main() 89 | -------------------------------------------------------------------------------- /Douban/DoubanBook.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import requests,re 3 | from bs4 import BeautifulSoup 4 | import leancloud 5 | from leancloud import Object 6 | import time 7 | 8 | class Book: 9 | def __init__(self): 10 | leancloud.init("=====", "======") 11 | self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 12 | self.headers = {'User-Agent': self.user_agent} 13 | 14 | def getContent(self,url): 15 | reqContent = requests.get(url, headers=self.headers) 16 | if reqContent.status_code == 200: 17 | i = 0 18 | soup = BeautifulSoup(reqContent.content, 'html.parser') 19 | for link in soup.find_all('tr', 'item'): 20 | bookInfo = BookSave() 21 | print i 22 | try: 23 | bookInfo.set('bookIntr', self.getBookIntro(link.a['href'])).save() 24 | bookInfo.set('imgUrl', link.find('a', 'nbg').contents[1]['src']).save() 25 | bookInfo.set('bookTitle', link.find('div', 'pl2').a['title']).save() 26 | bookInfo.set('bookAuth', link.find('p', 'pl').string).save() 27 | bookInfo.set('bookRate', link.find('span', 'rating_nums').string).save() 28 | bookInfo.set('rateNum', link.find('span', 'pl').string[1:-2].strip()).save() 29 | except leancloud.LeanCloudError as e: 30 | print e.message 31 | i = i + 1 32 | ''' 33 | print self.getBookIntro(link.a['href']) 34 | print link.find('a', 'nbg').contents[1]['src'] 35 | print link.find('div', 'pl2').a['title'] 36 | print link.find('p', 'pl').string 37 | print link.find('span', 'rating_nums').string 38 | print link.find('span', 'pl').string[1:-2].strip() 39 | ''' 40 | 41 | def getBookIntro(self, url): 42 | intrContent = requests.get(url, headers=self.headers) 43 | if intrContent.status_code == 200: 44 | pattern4 = re.compile('
.*?.*?
(.*?)
', re.S) 45 | BookIntro = re.findall(pattern4, intrContent.content) 46 | if(len(BookIntro) > 0): 47 | bookInfro = BookIntro[0].strip().decode('utf-8').replace('

',' ').replace('

',' ') 48 | else: 49 | print 'data error' 50 | if(len(bookInfro) > 0): 51 | return bookInfro 52 | else: 53 | print 'no data' 54 | def start(self): 55 | for page in range(5,10): 56 | if(page == 0): 57 | baseUrl = 'https://book.douban.com/top250' 58 | self.getContent(baseUrl) 59 | print 'page 1 ok' 60 | else: 61 | baseUrl = 'https://book.douban.com/top250?start=' 62 | pages = page * 25 63 | url = baseUrl + str(pages) 64 | self.getContent(url) 65 | time.sleep(5) 66 | print 'page', page, 'ok' 67 | 68 | 69 | class BookSave(Object): 70 | # bookimgurl 71 | @property 72 | def imgUrl(self): 73 | return self.get('imgUrl') 74 | @imgUrl.setter 75 | def imgUrl(self, value): 76 | return self.set('imgUrl', value) 77 | 78 | # bookIntr 79 | @property 80 | def bookIntr(self): 81 | return self.get('bookIntr') 82 | @bookIntr.setter 83 | def bookIntr(self, value): 84 | return self.set('bookIntr', value) 85 | 86 | # bookTitle 87 | @property 88 | def bookTitle(self): 89 | return self.get('bookTitle') 90 | @bookTitle.setter 91 | def bookTitle(self, value): 92 | return self.set('bookTitle', value) 93 | 94 | # bookAuth 95 | @property 96 | def bookAuth(self): 97 | return self.get('bookAuth') 98 | @bookAuth.setter 99 | def bookAuth(self, value): 100 | return self.set('bookAuth', value) 101 | 102 | # bookRate 103 | @property 104 | def bookRate(self): 105 | return self.get('bookRate') 106 | @bookRate.setter 107 | def bookRate(self, value): 108 | return self.set('bookRate', value) 109 | 110 | # rateNum 111 | @property 112 | def rateNum(self): 113 | return self.get('rateNum') 114 | @rateNum.setter 115 | def rateNum(self, value): 116 | return self.set('rateNum', value) 117 | 118 | 119 | 120 | book = Book() 121 | book.start() 122 | -------------------------------------------------------------------------------- /BaiDu/GetPicture.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'Wu_cf' 3 | 4 | import json 5 | import itertools 6 | import urllib 7 | import requests 8 | import os 9 | import re 10 | import sys 11 | sys.path.append('PIL') 12 | from PIL import Image as im 13 | 14 | 15 | str_table = { 16 | '_z2C$q': ':', 17 | '_z&e3B': '.', 18 | 'AzdH3F': '/' 19 | } 20 | 21 | char_table = { 22 | 'w': 'a', 23 | 'k': 'b', 24 | 'v': 'c', 25 | '1': 'd', 26 | 'j': 'e', 27 | 'u': 'f', 28 | '2': 'g', 29 | 'i': 'h', 30 | 't': 'i', 31 | '3': 'j', 32 | 'h': 'k', 33 | 's': 'l', 34 | '4': 'm', 35 | 'g': 'n', 36 | '5': 'o', 37 | 'r': 'p', 38 | 'q': 'q', 39 | '6': 'r', 40 | 'f': 's', 41 | 'p': 't', 42 | '7': 'u', 43 | 'e': 'v', 44 | 'o': 'w', 45 | '8': '1', 46 | 'd': '2', 47 | 'n': '3', 48 | '9': '4', 49 | 'c': '5', 50 | 'm': '6', 51 | '0': '7', 52 | 'b': '8', 53 | 'l': '9', 54 | 'a': '0' 55 | } 56 | 57 | # str 的translate方法需要用单个字符的十进制unicode编码作为key 58 | # value 中的数字会被当成十进制unicode编码转换成字符 59 | # 也可以直接用字符串作为value 60 | char_table = {ord(key): ord(value) for key, value in char_table.items()} 61 | 62 | # 解码图片URL 63 | def decode(url): 64 | # 先替换字符串 65 | for key, value in str_table.items(): 66 | url = url.replace(key, value) 67 | # 再替换剩下的字符 68 | return url.translate(char_table) 69 | 70 | # 生成网址列表 71 | def buildUrls(word): 72 | #底下这种用法是python3.x 73 | # word = urllib.parse.quote(word) 74 | #底下这种用法是python2.x 75 | word = urllib.quote(word) 76 | url = r"http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&fp=result&queryWord={word}&cl=2&lm=-1&ie=utf-8&oe=utf-8&st=-1&ic=0&word={word}&face=0&istype=2nc=1&pn={pn}&rn=60" 77 | urls = (url.format(word=word, pn=x) for x in itertools.count(start=0, step=60)) 78 | return urls 79 | 80 | # 解析JSON获取图片URL 81 | re_url = re.compile(r'"objURL":"(.*?)"') 82 | def resolveImgUrl(html): 83 | imgUrls = [decode(x) for x in re_url.findall(html)] 84 | return imgUrls 85 | 86 | def downImg(imgUrl, dirpath, imgName): 87 | filename = os.path.join(dirpath, imgName) 88 | try: 89 | res = requests.get(imgUrl, timeout=15) 90 | if str(res.status_code)[0] == "4": 91 | print str(res.status_code), ":" , imgUrl 92 | return False 93 | except Exception as e: 94 | print "抛出异常:", imgUrl 95 | print e 96 | return False 97 | #图片写入文件 98 | with open(filename, "wb") as f: 99 | f.write(res.content) 100 | return True 101 | 102 | 103 | def mkDir(dirName): 104 | dirpath = os.path.join(sys.path[0], dirName) 105 | if not os.path.exists(dirpath): 106 | os.mkdir(dirpath) 107 | return dirpath 108 | 109 | def selectImg(): 110 | # 这里路径换成你自己的 111 | path = 'C:\\Users\\Administrator\\PycharmProjects\\self_spiderLearning\\results\\' 112 | for x in os.listdir(path): 113 | if x.endswith('.jpg'): 114 | # file是图像对象,并没有close()方法 115 | try: 116 | fp = open(path+x,'rb') 117 | file = im.open(fp) 118 | except Exception as e: 119 | continue 120 | x1= file.size[0] 121 | y1= file.size[1] 122 | print x1,y1 ,x 123 | fp.close() 124 | if x1 < y1: 125 | try: 126 | os.remove(path+x) 127 | print "已删除 %s " % x 128 | except Exception as e: 129 | print "抛出异常:" 130 | print e 131 | 132 | 133 | 134 | 135 | if __name__ == '__main__': 136 | print "欢迎使用百度图片下载脚本!\n目前仅支持单个关键词。" 137 | print "下载结果保存在脚本目录下的results文件夹中。" 138 | print "=" * 50 139 | word = raw_input("请输入你要下载的图片关键词:\n") 140 | 141 | count = raw_input("请输入你要下载的图片的数目:\n") 142 | #创建保存图片的路径 143 | dirpath = mkDir("results") 144 | 145 | urls = buildUrls(word) 146 | index = 0 147 | flag = 1 148 | for url in urls: 149 | if flag == 2: 150 | break 151 | print "正在请求:", url 152 | html = requests.get(url, timeout=10).content.decode('utf-8') 153 | imgUrls = resolveImgUrl(html) 154 | if len(imgUrls) == 0: # 没有图片则结束 155 | break 156 | for url in imgUrls: 157 | 158 | if downImg(url, dirpath, str(index) + ".jpg"): 159 | index += 1 160 | #print type(index),type(count) 161 | if index > int(count): 162 | flag = 2 163 | break 164 | print "已下载 %s 张" % index 165 | print u'开始执行选择图片程序' 166 | selectImg() 167 | -------------------------------------------------------------------------------- /ZhiHu/GetImgs.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'Wu_cf' 3 | from auth import islogin 4 | from auth import Logging 5 | import requests,cookielib,sys,urllib,urllib2,os,json,re 6 | from bs4 import BeautifulSoup 7 | basePath = r'F:\SPIDER_IMGS' 8 | requests = requests.Session() 9 | requests.cookies = cookielib.LWPCookieJar('cookies') 10 | 11 | try: 12 | requests.cookies.load(ignore_discard=True) 13 | except: 14 | Logging.error(u"你还没有登录知乎哦 ...") 15 | Logging.info(u"执行 `python auth.py` 即可以完成登录。") 16 | raise Exception("无权限(403)") 17 | if islogin() != True: 18 | Logging.error(u"你的身份信息已经失效,请重新生成身份信息( `python auth.py` )。") 19 | raise Exception("无权限(403)") 20 | reload(sys) 21 | sys.setdefaultencoding('utf8') 22 | url = 'http://www.zhihu.com/question/31159026' 23 | ''' 24 | 图片获取类 25 | ''' 26 | class GetPic: 27 | url = None 28 | def __init__(self,url): 29 | self.url = url 30 | r = requests.get(self.url) 31 | self.soup = BeautifulSoup(r.content,"lxml") 32 | print r.status_code 33 | ''' 34 | 获取中文标题 35 | ''' 36 | def getTitle(self): 37 | #replace() 将换行替换成空格 38 | title = self.soup.find("h2", class_="zm-item-title").string.replace("\n", "") 39 | return self 40 | ''' 41 | 获取答案数量,这个很重要,图片(图片url)就包含在答案中, 42 | ''' 43 | def getAnsNums(self): 44 | answers_num = 0 45 | if self.soup.find("h3", id="zh-question-answer-num") != None: 46 | #可以参考这个源码,取出里面的字段。。。 47 | answers_num = int(self.soup.find("h3", id="zh-question-answer-num")["data-num"]) 48 | print u"共有",answers_num,u"条回复" 49 | return answers_num 50 | ''' 51 | 下载图片,这里可能写的有点乱.注明一下:1:首先我们在还未点击加载更多时候,提取图片,即 if i== 0阶段 52 | 2:在我们点击了加载更多时候,提取图片(我们可以在点击加载更多时候,看看浏览器发送了什么内容) 53 | 3:具体的可以看http://lovenight.github.io这里的博客,写的非常详细,具体实现肯呢个不一样,但是思路都是一样的 54 | ''' 55 | def getAllImgs(self): 56 | answers_num = self.getAnsNums() 57 | img_counter = 0 58 | for i in xrange((answers_num - 1) / 50 + 1): 59 | if i == 0: 60 | img_list = self.soup.find_all("img", class_="origin_image zh-lightbox-thumb lazy") 61 | for img in img_list: 62 | img_url = img["data-original"] 63 | picname = str(img_counter) + '.jpg' 64 | filename = os.path.join(basePath,picname) 65 | urllib.urlretrieve(img_url,filename) 66 | img_counter += 1 67 | print u"第%s"%img_counter,u"下载成功" 68 | else: 69 | print u"===第",i,u"次xrf请求" 70 | post_url = "http://www.zhihu.com/node/QuestionAnswerListV2" 71 | urls = "http://www.zhihu.com/" 72 | r = requests.get(urls) 73 | results = re.compile(r"\ 12 | 13 | ## 一 . 爬取目标 14 | 15 | 打开豆瓣图书 Top250 首页: 16 | 17 | ![douban1.png](http://7xrl8j.com1.z0.glb.clouddn.com/douban1.png) 18 | 19 | 点击标题进去看看: 20 | 21 | ![douban2.png](http://7xrl8j.com1.z0.glb.clouddn.com/douban2.png) 22 | 23 | 如上截图所示即为要爬取的内容,如下列出: 24 | 25 | 1. 书籍内容简 url 26 | 2. 书籍封面 url 27 | 3. 书籍标题 28 | 4. 书籍作者 29 | 5. 书籍评分以及评价人数 30 | 6. 书籍简介内容 31 | 32 | ## 二 . 思路分析 33 | 34 | #### 2.1 元素提取分析 35 | 36 | 首先右键鼠标查看 HTML 源码,HTML 源码截图贴出如下: 37 | 38 | ![douban3.png](http://7xrl8j.com1.z0.glb.clouddn.com/douban3.png) 39 | 40 | 如上截图。我们查看页面源码时候发现每个书籍 Item 所对应的源码 Item 都如上所示。所以我们提取元素时候只需要研究如上一个 Item 即可。其他剩下的可以循环获取。 41 | 42 | 如上截图所标注数字即对应我们要提取的元素。而对于**书籍简介内容**,我们在获取标注 1 之后仍然要去请求一下。请求之后获取的 HTML 源码截图如下: 43 | 44 | ![douban4.png](http://7xrl8j.com1.z0.glb.clouddn.com/douban4.png) 45 | 46 | #### 2.2 页面请求分析 47 | 48 | 打开豆瓣图书 Top250 , 浏览器地址栏 url 如下截图所示: 49 | 50 | ![douban12.png](http://7xrl8j.com1.z0.glb.clouddn.com/douban12.png) 51 | 52 | 可以认为其为页面 BaseUrl。然后我们拉到页面最低端**点击下一页**看看地址栏有没有变化,截图如下所示: 53 | 54 | ![douban13.png](http://7xrl8j.com1.z0.glb.clouddn.com/douban13.png) 55 | 56 | 发现多了一个 ?start = 25 。而恰好每个页面的 Item 都是 25 个。我们猜想是不是只需要变化 25 这个数字所在位置的参数即可?我们将 25 改成 50 。发现确实会调转到下一个页面。**验证猜想。** 57 | 58 | ![douban11.png](http://7xrl8j.com1.z0.glb.clouddn.com/douban11.png) 59 | 60 | 注: 如果当时我们点击下一页的时候地址栏没有发生参数变化,那么我们就要 F12 了,从后台看看请求过程发生了什么。 61 | 62 | 以上我们分析了请求过程。那么我们真正爬取的时候参数从 0 (*25)- 9(*25) 即可。 63 | 64 | ## 三 . 爬取源码分析 65 | 66 | ```python 67 | # 提取主要内容 68 | def getContent(self,url): 69 | reqContent = requests.get(url, headers=self.headers) 70 | if reqContent.status_code == 200: 71 | i = 0 72 | soup = BeautifulSoup(reqContent.content, 'html.parser') 73 | # 循环获取一个页面的所有 Item 然后进行内部元素提取 74 | for link in soup.find_all('tr', 'item'): 75 | # 构建云端存储对象 76 | bookInfo = BookSave() 77 | print 'num',i-1,' data save success' 78 | try: 79 | # 用 bs 来提取相应元素并存储至云端 80 | bookInfo.set('bookIntr', self.getBookIntro(link.a['href'])).save() 81 | bookInfo.set('imgUrl', link.find('a', 'nbg').contents[1]['src']).save() 82 | bookInfo.set('bookTitle', link.find('div', 'pl2').a['title']).save() 83 | bookInfo.set('bookAuth', link.find('p', 'pl').string).save() 84 | bookInfo.set('bookRate', link.find('span', 'rating_nums').string).save() 85 | bookInfo.set('rateNum', link.find('span', 'pl').string[1:-2].strip()).save() 86 | except leancloud.LeanCloudError as e: 87 | print e.message 88 | i = i + 1 89 | 90 | # 提取书籍介绍内容 91 | def getBookIntro(self, url): 92 | # 请求书籍介绍内容页面 93 | intrContent = requests.get(url, headers=self.headers) 94 | if intrContent.status_code == 200: 95 | # 用 re 来提取书籍介绍内容 96 | pattern4 = re.compile('
.*?.*?
(.*?)
', re.S) 97 | BookIntro = re.findall(pattern4, intrContent.content) 98 | if(len(BookIntro) > 0): 99 | bookInfro = BookIntro[0].strip().decode('utf-8').replace('

',' ').replace('

',' ') 100 | else: 101 | print 'data error' 102 | if(len(bookInfro) > 0): 103 | return bookInfro 104 | else: 105 | print 'no data' 106 | else: 107 | print 'error' 108 | 109 | # 启动函数 110 | def start(self): 111 | for page in range(5,10): 112 | if(page == 0): 113 | baseUrl = 'https://book.douban.com/top250' 114 | self.getContent(baseUrl) 115 | print 'page 1 ok' 116 | else: 117 | baseUrl = 'https://book.douban.com/top250?start=' 118 | pages = page * 25 119 | url = baseUrl + str(pages) 120 | self.getContent(url) 121 | time.sleep(3) 122 | print 'page', page, 'ok' 123 | ``` 124 | 125 | 如上即为整个爬取代码的核心部分。 126 | 127 | 1. Requests 库来进行网络请求。其会返回请求状态码。我们可以根据该状态码是否为 200 来判断是否请求成功。 128 | 2. BeautifulSoup 库和 re 来进行页面元素的提取。之所以在用到 bs 的情况下又用到 re ,是因为在提取书籍介绍内容的过程中有很多相同的 Item ,这时候用 bs 提取会获取的一些你不想获取的数据。所以用正则来层层向外扩张过滤。 129 | 3. 加上一个 time.spleep(3) 来防止被豆瓣识别出来。也是极其简单的策略。 130 | 4. 爬取的数据大多都是不规整的。比如空格,扩号,换行 等等。这时候我们就需要 python 中非常基础的字符串处理函数和正则**来进一步过滤数据了**。 131 | 5. 对于有一些你爬取的数据,十分有必要检查一下它的大小。因为极有可能在该数据为空的情况下,你对他进行一些字符串的操作处理。这样会导致一些不必要的异常发生,从而会导致爬虫终止。 132 | 133 | ## 四 . 个人总结 134 | 135 | 整个爬取过程还是很简单的。没有涉及到登录以及其他复杂的验证过程。主要提取出来的数据是为了 One App 服务。爬到数据就好了。比如速度和量都没有刻意追求。加上延时的话也才一分多钟就爬好了。另外文中也说了这里请求的 url 很简单。直接在地址栏告诉你规律。如果有的网站地址栏变化不告诉你,你就要去后台看看**在你拖动页面往下或者点击下一页**的时候 network 中有没有什么参数发生变化。一般都是 [XHR](http://baike.baidu.com/item/XMLHTTPRequest) 这个参数会发生变化。点进去看看就好了。 136 | 137 | 以上,**感谢阅读**。 -------------------------------------------------------------------------------- /LaGou/lagouCrawler.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import urllib,datetime 3 | import requests,re,time 4 | import pymongo 5 | import time,threading 6 | import simplejson as json 7 | import sys 8 | reload(sys) 9 | sys.setdefaultencoding('utf-8') 10 | 11 | class lagouCrawler: 12 | 13 | def __init__(self): 14 | 15 | global collection 16 | global db 17 | client = pymongo.MongoClient("localhost", 27017) 18 | db = client.lagouDB 19 | 20 | def insert_data(self,data): 21 | 22 | data['_id'] = data['positionId'] 23 | data['updateTime'] = datetime.datetime.now() 24 | # 防止重复插入 25 | db.Collection.update_one( 26 | filter={'_id': data['_id']}, 27 | update={'$set': data}, 28 | upsert=True 29 | ) 30 | count = db.Collection.count() 31 | print u'已经存储了:',count,u'条记录' 32 | 33 | 34 | def get_data(self,url,pageNum,keyWord): 35 | 36 | page_headers = { 37 | 'Host': 'www.lagou.com', 38 | 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 39 | 'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3', 40 | 'Connection': 'keep-alive' 41 | } 42 | # 构建表单 43 | play_load = {'first': 'false', 'pn': pageNum, 'kd': keyWord} 44 | # 代理容易失效,自己替换 45 | proxies = { 46 | "http": "http://42.159.251.84:41795", 47 | "http": "http://121.33.226.167:3128", 48 | 'http': 'http://121.61.96.241:8118', 49 | 'http': 'http://39.1.46.165:8080' 50 | } 51 | # requests 请求 52 | req = requests.post(url, headers=page_headers, params=play_load) 53 | # 如果 pageNo == 0 表示后面没有页数了,不用继续往后面请求了 54 | if req.json()['content']['pageNo'] == 0: 55 | flag = True 56 | return flag 57 | 58 | # 如果 status_code == 200 表示请求正常 59 | if req.status_code == 200: 60 | req = req.json() 61 | 62 | if (('content' in req)): 63 | list_con = req['content']['positionResult']['result'] 64 | if len(list_con) >= 0: 65 | for i in list_con: 66 | # 构建存储字段 67 | formatData = { 68 | "companyShortName": i['companyShortName'], 69 | "salary":i['salary'], 70 | "city": i['city'], 71 | "education": i['education'], 72 | "positionName": i['positionName'], 73 | "workYear": i['workYear'], 74 | "companySize": i['companySize'], 75 | "financeStage": i['financeStage'], 76 | "industryField": i['industryField'], 77 | "positionId":i['positionId'] 78 | } 79 | 80 | lagouCrawler.insert_data(formatData) 81 | 82 | else: 83 | print u'数据错误:',req 84 | 85 | else: 86 | print u'网络不好,返回状态码:',req.status_code 87 | 88 | def start(self): 89 | 90 | for hy in hangye: 91 | print u'当前行业是:', hy 92 | # 中文字符转换成16进制 93 | keyWord1 = urllib.quote(hy) 94 | for i in citys: 95 | print u'当前城市是:',i 96 | keyWord2 = urllib.quote(i) 97 | url = 'http://www.lagou.com/jobs/positionAjax.json?hy={hy}&px=default&city={city}&needAddtionalResult=false' 98 | url = url.format(hy=keyWord1,city=keyWord2) 99 | for positionItem in positions: 100 | print u'当前职位是:', positionItem 101 | for page in xrange(1, 31): 102 | print u'当前抓取页面是:', page 103 | time.sleep(10) 104 | infoItem = lagouCrawler.get_data(url, page, positionItem) 105 | if infoItem == True: 106 | break 107 | print u'抓取完毕。' 108 | 109 | if __name__ == '__main__': 110 | 111 | count = 0 112 | 113 | # 职位 114 | positions = ['HTML5', 'Android', 'Python', 'PHP','.NET', 'C#', 'C++', 'C', 'VB', 'Perl', 'Ruby', 'Hadoop', 'Node.js', 'Go', 115 | 'ASP', 'Shell', '自然语言处理', '搜索推荐', '精准推荐','技术经理', '架构师', '测试', '技术总监', 116 | 'IOS', 'JavaScript', '网络工程师', 'UI', 'UE', '数据分析', 'MongoDB', 'MySql', 'SQLServer', 'Oracle', 117 | '运维工程师', 'WEB安全', '网络安全','数据挖掘', 'Java','爬虫工程师'] 118 | 119 | # 城市 120 | citys = ['北京', '上海', '深圳', '广州', '杭州', '成都', '南京', '武汉', '西安', '厦门', '长沙', '苏州', '天津', 121 | '重庆', '合肥', '济南', '大连', '珠海', '宁波', '中山'] 122 | 123 | # 行业 124 | hangye = ['教育','文化娱乐','移动互联网','游戏','O2O','硬件','社交网络','旅游',' 医疗健康','生活服务','信息安全',' 数据服务','广告营销','分类信息','电子商务','金融','企业服务',] 125 | 126 | lagouCrawler = lagouCrawler() 127 | lagouCrawler.start() 128 | 129 | -------------------------------------------------------------------------------- /One/one.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from bs4 import BeautifulSoup 3 | import requests,re 4 | import leancloud 5 | from leancloud import Object 6 | import time 7 | 8 | ''' 9 | 爬虫类,用来从 one 上抓取图片,问题以及回答 10 | ''' 11 | class One: 12 | def __init__(self): 13 | leancloud.init("xxxx", "xxxx") 14 | self.pageIndex = 1 15 | self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 16 | self.headers = {'User-Agent': self.user_agent} 17 | 18 | def getContent(self,url): 19 | req = requests.get(url,headers=self.headers) 20 | if req.status_code == 200: 21 | oneImgs = OneImg() 22 | # 图片 URL 23 | pattern0 = re.compile('
.*?.*?
',re.S) 24 | imgItem = re.findall(pattern0, req.content) 25 | oneImgs.set('imgUrl',imgItem[0]).save() 26 | # 简介描述 27 | pattern1 = re.compile('
(.*?)
',re.S) 28 | intrItem = re.findall(pattern1, req.content) 29 | oneImgs.set('imgIntr', intrItem[0]).save() 30 | # 作者 31 | pattern2 = re.compile('
(.*?)
',re.S) 32 | authItem = re.findall(pattern2, req.content) 33 | replaceBR = re.compile('
') 34 | authItem = re.sub(replaceBR, "\n", authItem[0]) 35 | oneImgs.set('imgAuth', authItem).save() 36 | # 日期 37 | pattern3 = re.compile('

(.*?)

.*?

(.*?)

',re.S) 38 | timeItem = re.findall(pattern3, req.content) 39 | oneImgs.set('imgDate', timeItem[0][0]+timeItem[0][1]).save() 40 | else: 41 | print '这一页失败了' 42 | 43 | 44 | def getQA(self,url): 45 | reqContent = requests.get(url,headers=self.headers) 46 | if reqContent.status_code == 200: 47 | oneQa = OneQa() 48 | # 获取问题以及简介 49 | pattern = re.compile( 50 | '
.*?
.*?

(.*?)

.*?
(.*?)
', re.S) 51 | questionTtems = re.findall(pattern, reqContent.content) 52 | for questionTtem in questionTtems: 53 | oneQa.set('qaIntr',questionTtem[0]).save() 54 | oneQa.set('qaDetail',questionTtem[1]).save() 55 | # 获取回答 56 | pattern2 = re.compile( 57 | '
.*?
.*?

(.*?)

.*?
(.*?)
', re.S) 58 | answerItems = re.findall(pattern2, reqContent.content) 59 | for answerItem in answerItems: 60 | replaceBR = re.compile('
') 61 | answer = re.sub(replaceBR, "\n", answerItem[1]) 62 | oneQa.set('qaAnsw', answer).save() 63 | else: 64 | print '这一页失败了' 65 | 66 | def start(self): 67 | baseUrl = 'http://wufazhuce.com/question/' 68 | baseUrl1 = 'http://wufazhuce.com/one/' 69 | for pageNum in range(1350,1410): 70 | print '第',pageNum,'页ok' 71 | url = baseUrl + str(pageNum) 72 | url1 = baseUrl1 + str(pageNum) 73 | time.sleep(6) 74 | self.getQA(url) 75 | self.getContent(url1) 76 | 77 | ''' 78 | 实体类用来存储到 Leancloud 79 | ''' 80 | class OneImg(Object): 81 | # imgUrl 82 | @property 83 | def imgUrl(self): 84 | return self.get('imgUrl') 85 | @imgUrl.setter 86 | def imgUrl(self, value): 87 | return self.set('imgUrl', value) 88 | # imgIntr 89 | @property 90 | def imgIntr(self): 91 | return self.get('imgIntr') 92 | @imgIntr.setter 93 | def imgIntr(self, value): 94 | return self.set('imgIntr', value) 95 | # imgAuth 96 | @property 97 | def imgAuth(self): 98 | return self.get('imgAuth') 99 | @imgAuth.setter 100 | def imgAuth(self, value): 101 | return self.set('imgAuth', value) 102 | # imgDate 103 | @property 104 | def imgDate(self): 105 | return self.get('imgDate') 106 | @imgDate.setter 107 | def imgDate(self, value): 108 | return self.set('imgDate', value) 109 | 110 | class OneQa(Object): 111 | # qaIntr 112 | @property 113 | def qaIntr(self): 114 | return self.get('qaIntr') 115 | @qaIntr.setter 116 | def qaIntr(self, value): 117 | return self.set('qaIntr', value) 118 | # qaDetail 119 | @property 120 | def qaDetail(self): 121 | return self.get('qaDetail') 122 | @qaDetail.setter 123 | def qaDetail(self, value): 124 | return self.set('qaDetail', value) 125 | # qaAnsw 126 | @property 127 | def qaAnsw(self): 128 | return self.get('qaAnsw') 129 | @qaAnsw.setter 130 | def qaAnsw(self, value): 131 | return self.set('qaAnsw', value) 132 | 133 | 134 | one = One() 135 | one.start() 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /ZhiHu/FirstLogin.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'Wu_cf' 3 | # Build-in / Std 4 | import os, sys, time, platform, random 5 | import re, json, cookielib 6 | import requests, termcolor 7 | import webbrowser; 8 | 9 | #会话形式,程序执行会在这里检查一下cookie 10 | requests = requests.Session() 11 | requests.cookies = cookielib.LWPCookieJar('cookies') 12 | 13 | try: 14 | requests.cookies.load(ignore_discard=True) 15 | except: 16 | pass 17 | 18 | def download_captcha(): 19 | url = "http://www.zhihu.com/captcha.gif" 20 | #请求验证码的url http://www.zhihu.com/captcha.gif?r=1448195049209 21 | r = requests.get(url, params={"r": random.random()} ) 22 | #请求成功会返回code 23 | if int(r.status_code) != 200: 24 | print u"验证码请求失败" 25 | image_name = u"verify." + r.headers['content-type'].split("/")[1] 26 | #wb:以二进制模式读写(图片嘛,很自然这样) 27 | open( image_name, "wb").write(r.content) 28 | #调用浏览器打开gif图片,接着自己输入就可以啦 29 | webbrowser.open(image_name) 30 | captcha_code = raw_input( u"请输入验证码: ") 31 | return captcha_code 32 | 33 | def search_xsrf(): 34 | url = "http://www.zhihu.com/" 35 | r = requests.get(url) 36 | if int(r.status_code) != 200: 37 | print u"验证码请求失败" 38 | # 39 | #就是匹配这一段的正则,\s是匹配任意空白字段符,S+匹配字符任意次 40 | results = re.compile(r"\(.*?)', pagehtml, re.S) 51 | 52 | for topic in topic_items: 53 | link = topic[0] 54 | name = topic[1] 55 | #topic_num = topic_num +1 56 | print link,name,topic_counter 57 | topic_counter = topic_counter + 1 58 | getallquestion.get_all_top_questions(name,link) 59 | else: 60 | print '=======',i 61 | post_url = "http://www.zhihu.com/topic" 62 | urls = "http://www.zhihu.com/" 63 | r = requests.get(urls) 64 | results = re.compile(r"\(.*?)', answer_list[j], re.S) 84 | for topic in topic_items: 85 | link = topic[0] 86 | name = topic[1] 87 | print link,name,topic_counter 88 | getallquestion.get_all_top_questions(name,link) 89 | topic_counter = topic_counter + 1 90 | print topic_counter 91 | return self 92 | class GetQuestions: 93 | '''1:根据话题来获得问题 94 | 话题要不要存入进数据库或者其他什么方式 95 | 3:http://www.zhihu.com/topic/19550523/top-answers?page=2 96 | ''' 97 | def get_all_top_questions(self,name,link): 98 | #每隔话题爬10个页面吧 99 | question_num = 0 100 | default_url = 'http://www.zhihu.com/topic/' + link + '/top-answers' 101 | #question_url = 'http://www.zhihu.com/topic/' + link + '/top-answers?page=' + str(i) 102 | print u"话题为",name 103 | for i in xrange(10): 104 | 105 | if i == 0: 106 | print "=====页面",i 107 | try: 108 | r = requests.get(default_url) 109 | except Exception as e: 110 | pass 111 | question_items = re.findall(r'

(.*?)

', r.content, re.S) 112 | for question in question_items: 113 | question_num = question_num + 1 114 | print question[1]," ",question_num 115 | 116 | else: 117 | print "=====页面",i 118 | changed_url = 'http://www.zhihu.com/topic/' + link + '/top-answers?page=' + str(i) 119 | try: 120 | r = requests.get(changed_url) 121 | except Exception as e: 122 | pass 123 | question_items = re.findall(r'

(.*?)

', r.content, re.S) 124 | for question in question_items: 125 | question_num = question_num + 1 126 | print question[1]," ",question_num 127 | def main(): 128 | url = "http://www.zhihu.com/topic" 129 | gettopics = GetTopics(url) 130 | test = gettopics.test() 131 | gettopicsnum = gettopics.get_foucustopic_num() 132 | gettopicname = gettopics.getAll_topic_link_name() 133 | if __name__ == '__main__': 134 | main() 135 | -------------------------------------------------------------------------------- /LuoWang/GetSong.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'Wu_cf' 3 | 4 | 5 | import os,time 6 | import sys 7 | import getopt 8 | import requests 9 | import unicodedata 10 | import eyed3 11 | import re 12 | from bs4 import BeautifulSoup 13 | 14 | LUOO_URL = "http://www.luoo.net/music/{}" 15 | SONG_NAME = "{}.mp3" 16 | SONG_NAME_OLD = "{} -{}.mp3" 17 | basePath = os.path.join(os.getcwd(), r'luowang') 18 | reload(sys) 19 | sys.setdefaultencoding('utf-8') 20 | DOWNLOAD_MODE = 0 # 0:输入模式 1:旧版本修复模式 2:更新模式 3:精准模式(只下载指定专辑的指定歌曲) 21 | song_shoot = 0 #精准模式使用,指定(某个专辑的)歌曲 22 | 23 | def get_mp3url(index): 24 | baseurl = u"http://luoo-mp3.kssws.ks-cdn.com/low" 25 | if index == 544 or index == 566 or index == 567 or index == 568:#这四个页面并不存在 26 | return u"" 27 | elif index == 497: 28 | return baseurl + u"/luoo/s1/{}.mp3" 29 | elif index >= 498 and index <= 521: 30 | return baseurl + u"/luoo/S" + str(index - 496) + u"/{}.mp3" 31 | elif index == 522: 32 | return baseurl + u"/luoo/s26/{}.mp3" 33 | elif index >= 523 and index <= 539: 34 | return baseurl + u"/anbai/radio" + str(index - 522) + u"/{}.mp3" 35 | elif index >= 540 and index <= 543: 36 | return baseurl + u"/china/radio" + str(index - 539) + u"/{}.mp3" 37 | elif index == 545: 38 | return baseurl + u"/china/radio5/{}.mp3" 39 | elif index >= 546 and index <= 557: 40 | return baseurl + u"/world/radio" + str(index - 545) + u"/{}.mp3" 41 | elif index == 558 or index == 559: 42 | return baseurl + u"/electric/radio" + str(index - 557) + u"/{}.mp3" 43 | elif index == 560 or index == 561: 44 | return baseurl + u"/classical/radio" + str(index - 559) + u"/{}.mp3" 45 | elif index >= 562 and index <= 565: 46 | return baseurl + u"/jazz/radio" + str(index - 561) + u"/{}.mp3" 47 | elif index == 569: 48 | return baseurl + u"/electric/radio3/{}.mp3" 49 | elif index == 573: 50 | return baseurl + u"/luoo/radio499/{}.mp3" 51 | elif index == 576: 52 | return baseurl + u"/jazz/radio5/{}.mp3" 53 | elif index == 581: 54 | return baseurl + u"/luoo/radio500/{}.mp3" 55 | elif index == 582: 56 | return baseurl + u"/luoo/radio581/{}.mp3" 57 | elif index == 583: 58 | return baseurl + u"/anbai/radio18/{}.mp3" 59 | elif index == 594: 60 | return baseurl + u"/anbai/radio19/{}.mp3" 61 | else: 62 | return baseurl + u"/luoo/radio"+str(index)+"/{}.mp3" 63 | 64 | def save_page(page,dir_name): 65 | dir_name = re.sub(r'[|\\?*<\":>+\[\]\/\']', u'_', dir_name) 66 | #创建存储目录 67 | filename = os.path.join(basePath,dir_name) 68 | 69 | if not os.path.exists(filename): 70 | os.makedirs(filename) 71 | # 转移到当前工作目录 72 | os.chdir(filename) 73 | #保存页面内容 74 | with open(dir_name+".html", 'w') as infofile: 75 | infofile.write(page) 76 | infofile.close() 77 | 78 | def repair_page(page,old_name,dir_name): 79 | dir_name = re.sub(r'[|\\?*<\":>+\[\]\/\']', u'_', dir_name) 80 | #将旧目录名改为新的 81 | oldfilename = os.path.join(basePath,old_name) 82 | newfilename = os.path.join(basePath,dir_name) 83 | 84 | if not os.path.exists(oldfilename): 85 | print u"文件目录不存在" + oldfilename 86 | return 87 | os.rename(oldfilename,newfilename) 88 | # 转移到当前工作目录 89 | os.chdir(newfilename) 90 | #保存页面内容 91 | with open(dir_name+".html", 'w') as infofile: 92 | infofile.write(page) 93 | infofile.close() 94 | 95 | 96 | 97 | def get_song_list(volumn): 98 | 99 | # **** http://www.luoo.net/music/801 100 | r = requests.get(LUOO_URL.format(volumn)) 101 | 102 | bs = BeautifulSoup(r.content, 'html.parser') 103 | try: 104 | name_number = bs.find("span","vol-number rounded").getText() 105 | except: 106 | print u"刊号: " + volumn + u" 不存在" 107 | return []#没有页面,直接返回(544,566,567,568) 108 | name_title = u"" 109 | name_sharp = u"" 110 | try: 111 | name_title = bs.find("span","vol-title").getText() 112 | name_sharp = bs.find("a","vol-tag-item").getText() 113 | except: 114 | pass 115 | if DOWNLOAD_MODE == 1: 116 | repair_page(r.content, name_number,name_number + u" " + name_title + u" " + name_sharp) 117 | else: 118 | save_page(r.content, name_number + u" " + name_title + u" " + name_sharp) 119 | 120 | songs = bs.find_all('div', 'player-wrapper') 121 | print u"刊号: " + volumn + u" 基本信息取得完毕" 122 | result = [] 123 | # 获取歌曲目录列表 124 | index = 1 125 | for song in songs: 126 | meta = {} 127 | meta['name'] = song.find('p', 'name').getText() 128 | meta['name'] = re.sub(r'[|\\?*<\":>+\[\]\/\']', u'_', meta['name']) 129 | meta['artist'] = re.sub(ur'Artist: ', u"", song.find('p', 'artist').getText()) #去掉 artist 中 Artist: 130 | meta['oldartist'] = song.find('p', 'artist').getText() #修复模式使用 131 | meta['album'] = re.sub(ur'Album: ', u"", song.find('p', 'album').getText()) #去掉 album 中 Album: 132 | meta['image'] = song.find('img')['src'] #专辑图片 133 | if DOWNLOAD_MODE == 3:#精准模式,只下载一首 134 | if index == song_shoot: 135 | result.append(meta) 136 | else: 137 | result.append(meta) 138 | index += 1 139 | print u"刊号: " + volumn + u" 歌曲信息取得完毕,共 " +str(len(result)) + u" 首" 140 | return result 141 | 142 | def download_songs(volumn): 143 | 144 | songs = get_song_list(volumn) 145 | index = 0 146 | song_name = "" 147 | for song in songs: 148 | index += 1 149 | if DOWNLOAD_MODE == 3: 150 | track = '%02d' % song_shoot 151 | else: 152 | track = '%02d' % index 153 | song_name = SONG_NAME.format(song['name']) 154 | 155 | if DOWNLOAD_MODE == 1:#修复模式 156 | print u"刊号: " + volumn + u" " + str(index) + u"/" + str(len(songs)) + u" 开始修复: " + song['name'] 157 | song_name_old = SONG_NAME_OLD.format(song['name'], song['oldartist']) 158 | if os.path.isfile(song_name_old)==True: 159 | os.rename(song_name_old,song_name) 160 | else:#没有文件则跳到下一首 161 | print u"刊号: " + volumn + u" " + str(index) + u"/" + str(len(songs)) + u" 未找到文件:"+ song_name_old +u" 跳过" 162 | continue 163 | else: 164 | print u"刊号: " + volumn + u" " + str(index) + u"/" + str(len(songs)) + u" 开始下载: " + song['name'] 165 | # http://luoo-mp3.kssws.ks-cdn.com/low/luoo/radio801/01.mp3 166 | # volumn就是801页面,01就是歌曲标识 167 | try: 168 | mp3url = get_mp3url(int(volumn)) 169 | r = requests.get(mp3url.format(track), stream=True) 170 | if r.status_code != 200: 171 | if DOWNLOAD_MODE == 3: 172 | track = str(song_shoot) 173 | else: 174 | track = str(index) 175 | rs = requests.get(mp3url.format(track), stream=True) 176 | if rs.status_code != 200: 177 | print u"刊号: " + volumn + u" " + str(index) + u"/" + str(len(songs)) + u" 下载失败 " 178 | with open(str(index) + u"_error.txt", 'w') as fe: 179 | fe.write(u"") 180 | fe.close() 181 | return 182 | 183 | # Requests 获取头部响应流 184 | with open(song_name, 'wb') as fd: 185 | for chunk in r.iter_content(): 186 | fd.write(chunk) 187 | fd.close() 188 | except: 189 | print u"刊号: " + volumn + u" " + str(index) + u"/" + str(len(songs)) + u" 下载失败 " 190 | with open(str(index) + u"_error.txt", 'w') as fe: 191 | fe.write(u"") 192 | fe.close() 193 | try: 194 | #读取专辑封面 195 | coverimage = requests.get(song['image'],stream=False) 196 | #写入id3 197 | audiofile = eyed3.load(song_name) 198 | if audiofile.tag is None: 199 | audiofile.tag = eyed3.id3.Tag() 200 | audiofile.tag.file_info = eyed3.id3.FileInfo(song_name) 201 | audiofile.tag.artist = song['artist'] 202 | audiofile.tag.album = song['album'] 203 | audiofile.tag.album_artist = u"http://www.luoo.net" 204 | audiofile.tag.title = song['name'] 205 | if DOWNLOAD_MODE == 3: 206 | audiofile.tag.track_num = song_shoot 207 | else: 208 | audiofile.tag.track_num = index 209 | audiofile.tag.images.set(3,coverimage.content,"image/jpeg",u"") 210 | audiofile.tag.save(version=eyed3.id3.ID3_DEFAULT_VERSION,encoding='utf-8')#id3v1 不支持unicode ,使用id3v2 211 | except: 212 | pass#id3v2写入失败,无所谓 213 | 214 | if DOWNLOAD_MODE == 1: 215 | print u"刊号: " + volumn + u" " + str(index) + u"/" + str(len(songs)) + u" 修复完成" 216 | else: 217 | print u"刊号: " + volumn + u" " + str(index) + u"/" + str(len(songs)) + u" 下载完成" 218 | # 睡一下 219 | time.sleep(3); 220 | if DOWNLOAD_MODE == 1: 221 | print u"专辑 " + volumn + u" 修复完成" 222 | else: 223 | print u"专辑 " + volumn + u" 下载完成" 224 | 225 | def main(argv): 226 | #参数处理 227 | opts, args = getopt.getopt(argv, "hrf:t:s:e:uy") 228 | page_from = 0 229 | page_to = 0 230 | page_shoot = 0 231 | default_yes = False#更新模式使用,不需要确认更新 232 | global song_shoot 233 | global DOWNLOAD_MODE 234 | 235 | for op, value in opts: 236 | if op == "-f": 237 | DOWNLOAD_MODE = 0 238 | try: 239 | page_from = int(value) 240 | except : 241 | print u"参数有误: -f 开始期刊 请输入大于零的数字" 242 | sys.exit() 243 | elif op == "-t": 244 | DOWNLOAD_MODE = 0 245 | try: 246 | page_to = int(value) 247 | except : 248 | print u"参数有误: -t 停止期刊 请输入大于零的数字" 249 | sys.exit() 250 | elif op == "-h": 251 | print u"参数: \n-f 开始期刊 \n-t 停止期刊 \n\ 252 | -h 参数列表\n-r 旧版本修复\n-u 更新模式\n\ 253 | -s 精准模式:指定期刊\n-e精准模式:指定歌曲" 254 | sys.exit() 255 | elif op == "-r": 256 | DOWNLOAD_MODE = 1 257 | elif op == "-u": 258 | DOWNLOAD_MODE = 2 259 | elif op == "-y": 260 | default_yes = True 261 | elif op == "-s": 262 | DOWNLOAD_MODE = 3 263 | try: 264 | page_shoot = int(value) 265 | except : 266 | print u"参数有误: -s 指定期刊 请输入大于零的数字" 267 | sys.exit() 268 | elif op == "-e": 269 | DOWNLOAD_MODE = 3 270 | try: 271 | song_shoot = int(value) 272 | except : 273 | print u"参数有误: -e 指定歌曲 配合-s使用" 274 | sys.exit() 275 | #参数处理完毕 276 | 277 | if DOWNLOAD_MODE == 0 or DOWNLOAD_MODE == 1: 278 | #无 from ,to ,需要用户输入 279 | if page_from == 0 and page_to == 0: 280 | vol = "" 281 | print u"请输入音乐期刊号:\n>" 282 | while (page_from <= 0): 283 | vol = raw_input() 284 | try: 285 | page_from = int(vol) 286 | except : 287 | print u"值有误,请输入大于零的音乐期刊号:\n>" 288 | continue 289 | if page_from <= 0: 290 | print u"值有误,请输入大于零的音乐期刊号:\n>" 291 | 292 | if page_from <= 0 and page_to > 0: 293 | page_from = page_to 294 | if page_from > 0 and page_to <= 0: 295 | page_to = page_from 296 | if page_from > page_to :#有两个数字就行了,没必要让用户重新输入 297 | page_to,page_from = page_from,page_to 298 | 299 | if DOWNLOAD_MODE == 1: 300 | print u"准备修复刊号: " + str(page_from) + u" 到 " + str(page_to) 301 | else: 302 | print u"准备下载刊号: " + str(page_from) + u" 到 " + str(page_to) 303 | for i in range(page_from,page_to + 1): 304 | if DOWNLOAD_MODE == 1: 305 | print u"开始修复刊号: " + str(i) 306 | else: 307 | print u"开始下载刊号: " + str(i) 308 | download_songs(str(i)) 309 | if DOWNLOAD_MODE == 1: 310 | print u"期刊" + str(page_from) + u"到" + str(page_to) + u" 修复完成" 311 | else: 312 | print u"期刊" + str(page_from) + u"到" + str(page_to) + u" 下载完成" 313 | elif DOWNLOAD_MODE == 2:#更新模式 314 | #找到最新专辑序号 315 | r = requests.get(u"http://www.luoo.net/") 316 | bs = BeautifulSoup(r.content, 'html.parser') 317 | page_to = int(re.sub(ur'http://www.luoo.net/music/', u"", bs.find("a","cover-wrapper cover-wrapper-lg")['href'])) 318 | print u"最新期刊:" + str(page_to) 319 | #找到本地最新专辑序号 320 | result = [] 321 | if not os.path.exists(basePath):#什么都没有,从1开始到最新 322 | page_from = 1 323 | else: 324 | for file in os.listdir(basePath): 325 | try: 326 | index = int(file[0:3]) 327 | result.append(index) 328 | except : 329 | pass 330 | page_from = max(result) + 1 331 | if page_from == page_to + 1: 332 | print u"本地专辑是最新的" 333 | elif page_from > page_to + 1: 334 | print u"本地目录混乱,请清理后重试" 335 | else: 336 | print u"准备下载刊号: " + str(page_from) + u" 到 " + str(page_to) +u"确认更新吗?(y/n)" 337 | if default_yes == False: 338 | vol = raw_input() 339 | else: 340 | vol = u'y' 341 | 342 | if vol != u'y' and vol != u'Y': 343 | print u"更新取消" 344 | sys.exit() 345 | 346 | for i in range(page_from,page_to + 1): 347 | print u"开始下载刊号: " + str(i) 348 | download_songs(str(i)) 349 | print u"更新完毕" 350 | 351 | 352 | elif DOWNLOAD_MODE == 3:#精准模式 353 | print u"开始下载专辑 " + str(page_shoot) + u" 的第 " + str(song_shoot) + u" 首歌曲" 354 | download_songs(str(page_shoot)) 355 | print u"专辑 " + str(page_shoot) + u" 的第 " + str(song_shoot) + u" 首歌曲下载完毕" 356 | 357 | 358 | if __name__ == '__main__': 359 | main(sys.argv[1:]) 360 | --------------------------------------------------------------------------------