├── 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 |  11 | 12 | 如下 Form Data 皆为 Post 需要构建的表单。 13 | 14 |  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 |  16 | 17 | * 点击页面进入HTML元素页面,可以看见歌曲名字,专辑名,歌者,但是怎么没有歌曲资源的url? 18 | 19 |  20 | 21 | * 没有歌曲的url我们就不能下载,那么我们要看看我们每一次换歌曲,发生了什么请求,点击审查元素,如下结果。 22 | 23 |  24 | 25 | * 如上图所示一个Request url,我们复制进浏览器地址输入栏,发现如下所示,没错这就是我们下载的url了。 26 | 27 |  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 |  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 |  8 | 9 |  10 | 11 | 拖动页面。可以看到加载更多按钮。但是地址栏没有变化。点击 F12 调试模式。如 2 所示看见 http//isujin.com/page/3。容易猜想输入 n(n 正常合理范围之内) 也是可以获得数据的。并且如果返回数据成功,返回 statusCode == 200。这个状态码对我们非常有用。 12 | 13 |  14 | 15 | 点击页面,查看源码,红框内即为要爬取的字段内容。 16 | 17 |  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 |  6 | 7 | 如上即为 One 首页。挺简单的,但是我们要批量抓取数据。只是简单的首页展示并不能满足。看网页源码如下: 8 | 9 |  10 | 11 | 如网页源码所标注。目标应该就是我们的链接 url。我们将其复制进地址栏。果然验证猜想。另外几个圈起来的即为要抓取的字段。分别对应着:图片的 URL 地址,作者,日期,一句话简介。 12 | 13 |  14 | 15 | 而当初我们就是要抓起批量的数据。所以这个 1406 就是我们首先要开刀。容易猜想出他就是历史内容。为验证,我们随机输入 1230 。果然如下所示。再多验证几次。大抵如是。所以我们抓取的时候,页面可以自己指定范围。当然会有 404 的时候。到时候我们也有解决的办法。 16 | 17 |  18 | 19 | One 的文章源码结构也是如上分析。道理亦然。 20 | 21 |  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) 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 | * 对于抓取的数字内容含有 "',' ').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 |  18 | 19 | 点击标题进去看看: 20 | 21 |  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 |  39 | 40 | 如上截图。我们查看页面源码时候发现每个书籍 Item 所对应的源码 Item 都如上所示。所以我们提取元素时候只需要研究如上一个 Item 即可。其他剩下的可以循环获取。 41 | 42 | 如上截图所标注数字即对应我们要提取的元素。而对于**书籍简介内容**,我们在获取标注 1 之后仍然要去请求一下。请求之后获取的 HTML 源码截图如下: 43 | 44 |  45 | 46 | #### 2.2 页面请求分析 47 | 48 | 打开豆瓣图书 Top250 , 浏览器地址栏 url 如下截图所示: 49 | 50 |  51 | 52 | 可以认为其为页面 BaseUrl。然后我们拉到页面最低端**点击下一页**看看地址栏有没有变化,截图如下所示: 53 | 54 |  55 | 56 | 发现多了一个 ?start = 25 。而恰好每个页面的 Item 都是 25 个。我们猜想是不是只需要变化 25 这个数字所在位置的参数即可?我们将 25 改成 50 。发现确实会调转到下一个页面。**验证猜想。** 57 | 58 |  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('',' ').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) 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 | '.*?