通过Python爬虫爬取知乎某个问题下的图片
来源:互联网 发布:网络教育和函授哪个好 编辑:程序博客网 时间:2024/06/11 03:59
该爬虫的完整代码我把它放到了GitHub上,因为目前是在一点点的增加功能阶段,所以代码可能没有完善好,但是正常运行时没有问题的,欢迎拍砖,:)
GitHub:点击打开链接
该爬虫主要是通过requests来实现的,该模块完全可以很好的代替urllib和urllib2,而且功能更强大,详细可以看这里。同时也用到了pillow模块中的image对象,实现环境是Python2,不过在Python3上只需很小的改动就可以正常运行,等后续代码功能完善好后,我会把Python3的实现也整理一份出来放在GitHub上。
首先通过cookie模拟登陆到知乎,然后获取知乎某一个问题的链接,打开并获取该问题回答下的图片,然后保存到本地。我们先看下知乎中的网页html文本,
对于某一个用户的回答是这样的:红色方框中的标签是回答的具体内容标签
然后原始图片是在下面这个标签里,包含在上图红色方框的标签下:
我们在写正则表达式的时候只需匹配到这个标签,然后取出里面的url就可以了。具体的正则表达式如下,分为两部分,首先取出”zm-editable-content.."标签里的全部内容,然后在从中取出"data-actualsrc"的内容:
pattern = re.compile('<a class="author-link".*?<span title=.*?<div class="zh-summary.*?' + '<div class="zm-editable-content.*?>(.*?)</div>', re.S)
pattern = re.compile('data-actualsrc="(.*?)">', re.S)
然后我们通过模拟登陆到知乎,具体的模拟登陆解释可以看这里。
# -*-coding:utf-8 -*-import requestsfrom requests.adapters import HTTPAdapter# import urllib# import urllib2import cookielibimport reimport timeimport os.pathfrom PIL import Imageuser_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5)'headers = {'User-Agent': user_agent}session = requests.session()session.cookies = cookielib.LWPCookieJar(filename='cookies')try: session.cookies.load(ignore_discard=True)except: print "Cookie 未能加载"def get_xsrf(): '''_xsrf 是一个动态变化的参数''' index_url = "http://www.zhihu.com" index_page = session.get(index_url, headers=headers) html = index_page.text pattern = r'name="_xsrf" value="(.*?)"' _xsrf = re.findall(pattern, html) return _xsrf[0]def get_captcha(): t = str(int(time.time() * 1000)) captcha_url = 'http://www.zhihu.com/captcha.gif?r' + t + "&type=login" print captcha_url r = session.get(captcha_url, headers = headers) with open('captcha.jpg', 'wb') as f: f.write(r.content) f.close() try: im = Image.open('captcha.jpg') im.show() im.close() except: print u'captcha.jpg 所在目录:%s, 手动输入'% os.path.abspath('captcha.jpg') captcha = input("input captcha\n") return captchadef isLogin(): url = "https://www.zhihu.com/settings/profile" login_code = session.get(url, allow_redirects=False).status_code print "login code: ", login_code if int(x=login_code) == 200: return True else: return Falsedef login(secret, account): if isLogin(): print "已经登录" return if re.match(r"^1\d{10}$", account): print "手机号登陆\n" post_url = 'http://www.zhihu.com/login/phone_num' postdata = { '_xsrf': get_xsrf(), 'password': secret, 'remember_me': 'true', 'phone_num': account, } else: print '邮箱登录\n' post_url = 'http://www.zhihu.com/login/email' postdata = { '_xsrf': get_xsrf(), 'password': secret, 'remember_me': 'true', 'email': account, } try: login_page = session.post(post_url, data=postdata, headers=headers) login_code = login_page.text print login_page.status print login_code print 'what?' except: print '需要验证码' postdata['captcha'] = get_captcha() login_page = session.post(post_url, data=postdata, headers=headers) login_code = eval(login_page.text) #eval 从字符串中提取字典 u = login_code['msg'] session.cookies.save()def getPageCode(pageUrl): try: req = session.get(pageUrl, headers=headers) print req.request.headers return req.text except urllib2.URLError, e: if hasattr(e, 'reason'): print u"打开链接失败...", e.reason return None
登陆进去后,我们在打开某一个知乎问题链接,爬取里面的图片然后下载到本地目录,具体看下面的代码:注意在输入验证码的时候我们用的是input(),在Python2中用input()输入的时候,如果输入字符串,那么要在输入的字符上加上引号,否则会报错,如:“abcd"
最后就可以把图片保存到Picture这个目录下了,当然这个爬虫目前还可以做很多的改动,比如翻页功能,然后多线程下载之类的,后续改进后我在贴上来吧。def getPageCode(pageUrl): try: req = session.get(pageUrl, headers=headers) print req.request.headers return req.text except urllib2.URLError, e: if hasattr(e, 'reason'): print u"打开链接失败...", e.reason return Nonedef getImageUrl(pageUrl): pageCode = getPageCode(pageUrl) if not pageCode: print "打开网页链接失败.." return None pattern = re.compile('<a class="author-link".*?<span title=.*?<div class="zh-summary.*?' + '<div class="zm-editable-content.*?>(.*?)</div>', re.S) items = re.findall(pattern, pageCode) imagesUrl = [] pattern = re.compile('data-actualsrc="(.*?)">', re.S) for item in items: urls = re.findall(pattern, item) imagesUrl.extend(urls) for url in imagesUrl: print url return imagesUrldef saveImagesFromUrl(pageUrl, filePath): imagesUrl = getImageUrl(pageUrl) if not imagesUrl: print 'imagesUrl is empty' return nameNumber = 0; for image in imagesUrl: suffixNum = image.rfind('.') suffix = image[suffixNum:] fileName = filePath + os.sep + str(nameNumber) + suffix nameNumber += 1 print 'save in: ', fileName response = requests.get(image) contents = response.content try: with open(fileName, "wb") as pic: pic.write(contents) except IOError: print 'Io error'login('这里是密码','这里是你的知乎账户')saveImagesFromUrl('https://www.zhihu.com/question/46435597', '/Volumes/HDD/Picture')注:该代码目前只能爬取到知乎某个问题下第一页的回答内容。
=====================更新1:下面来增加爬取知乎时的翻页
知乎网在处理翻页的时候,不像糗事百科这种直接在网址后面加数字一二三就可以实现翻页了,而是向服务器发送post请求,然后服务器响应翻页请求。我们用谷歌开发者工具抓取来看看就清楚了,下面是打开某个问题第一页时的获取情况:
这里我们可以发现,在请求页面的时候,是向服务器请求一个连接,然后post的数据有method和params,然后我们把页面下拉到最下面点击加载更多时如下:
这是第一个QuestionAnswerListV2,是在我点击加载更多的时候产生的,里面的数据内容和第一个的基本一样,只是下面的参数“offset”偏移量不同,增加了10,可以看出在一个页面上显示了10条数据,当然这得是在我们登陆的情况下,没有登陆的话就无法继续往下执行了。
所以到这里我们就已经搞清楚了,在翻页时需要post的链接和data,那处理起来就轻松多了,下面就直接贴代码了,运行后有彩蛋喔,模拟登陆部分还是没变。
注意:下面这段代码虽然可以正常的使用了,而且设置了链接超时和重连,不过当你爬取的问题的回答数比较多的时候,可能会等得久一点。
def getImageUrl(): url = "https://www.zhihu.com/node/QuestionAnswerListV2" method = 'next' size = 10 allImageUrl = [] #循环直至爬完整个问题的回答 while(True): print '===========offset: ', size postdata = { 'method': 'next', 'params': '{"url_token":' + str(46435597) + ',"pagesize": "10",' +\ '"offset":' + str(size) + "}", '_xsrf':get_xsrf(), } size += 10 page = session.post(url, headers=headers, data=postdata) ret = eval(page.text) listMsg = ret['msg'] if not listMsg: print "图片URL获取完毕, 页数: ", (size-10)/10 return allImageUrl pattern = re.compile('data-actualsrc="(.*?)">', re.S) for pageUrl in listMsg: items = re.findall(pattern, pageUrl) for item in items: #这里去掉得到的图片URL中的转义字符'\\' imageUrl = item.replace("\\", "") allImageUrl.append(imageUrl)def saveImagesFromUrl(filePath): imagesUrl = getImageUrl() print "图片数: ", len(imageUrl) if not imagesUrl: print 'imagesUrl is empty' return nameNumber = 0; for image in imagesUrl: suffixNum = image.rfind('.') suffix = image[suffixNum:] fileName = filePath + os.sep + str(nameNumber) + suffix nameNumber += 1 try: # 设置超时重试次数及超时时间单位秒 session.mount(image, HTTPAdapter(max_retries=3)) response = session.get(image, timeout=20) contents = response.content with open(fileName, "wb") as pic: pic.write(contents) except IOError: print 'Io error' except requests.exceptions.ConnectionError: print '连接超时,URL: ', image print '图片下载完毕'login('这是你的知乎密码','这是你的知乎账户')saveImagesFromUrl('/Volumes/HDD/Picture')
下面这段代码改进了一下,开了两个线程来跑,就是一个简单的生产者消费者模型,一个线程负责从网页中提取图片的URL,一个线程负责下载和保存到文件中。运行起来没有问题,测试了下当爬到一千多张的时候就会有错,我猜想是知乎网做了反爬虫处理,所以后面的更新中打算采用代理来绕开服务器的拦截。
from threading import Threadfrom Queue import Queuequeue = Queue(50)filePath = '/Volumes/HDD/image'isRun = Trueclass GetImageURLThread(Thread): def run(self): url = "https://www.zhihu.com/node/QuestionAnswerListV2" method = 'next' size = 10 if not os.path.exists(filePath): os.makedirs(filePath) # 循环直至爬完整个问题的回答 while (True): print '===========offset: ', size postdata = { 'method': 'next', 'params': '{"url_token":' + str(34243513) + ',"pagesize": "10",' + \ '"offset":' + str(size) + "}", '_xsrf': get_xsrf(), } size += 10 page = session.post(url, headers=headers, data=postdata) ret = eval(page.text) listMsg = ret['msg'] if not listMsg: print "图片URL获取完毕, 页数: ", (size-10) / 10 queue.join() isRun = False break pattern = re.compile('data-actualsrc="(.*?)">', re.S) global queue for pageUrl in listMsg: items = re.findall(pattern, pageUrl) for item in items: # 这里去掉得到的图片URL中的转义字符'\\' imageUrl = item.replace("\\", "") queue.put(imageUrl)class DownloadImgAndWriteToFile(Thread): def run(self): nameNumber = 0 global queue while isRun: image = queue.get() queue.task_done() suffixNum = image.rfind('.') suffix = image[suffixNum:] fileName = filePath + os.sep + str(nameNumber) + suffix nameNumber += 1 try: # 设置超时重试次数及超时时间单位秒 session.mount(image, HTTPAdapter(max_retries=3)) response = session.get(image, timeout=20) contents = response.content with open(fileName, "wb") as pic: pic.write(contents) except requests.exceptions.ConnectionError: print '连接超时,URL: ', image except IOError: print 'Io error' print '图片下载完毕'if __name__ == '__main__': login('这是你的知乎密码', '这是你的知乎账户') urlThread = GetImageURLThread() downloadThread = DownloadImgAndWriteToFile() urlThread.start() downloadThread.start() urlThread.join() downloadThread.join()
===============更新2,设置代理,Python爬虫设置代理IP爬取知乎图片
效果类似这样:
3 0
- 通过Python爬虫爬取知乎某个问题下的图片
- python下载知乎某个问题下的全部图片
- 爬虫之爬取知乎下某个问题下的全部图片(处理AJAX请求,解析json数据)
- 爬去知乎下某个问题下所有的图片
- Python爬虫对知乎问题下的图片进行爬取
- [Python 爬虫之路4] 使用selenium爬取知乎任意一个问题下,所有回答中的图片
- python下多线程爬虫爬取斗图网的所有最新图片
- python爬虫:爬取豆瓣读书某个tag下的书籍并存入excel
- python爬虫之反爬虫情况下的煎蛋网图片爬取初步探索
- python Requests 知乎问题图片爬虫
- python 遍历某个路径下的所有图片并将图片路径保存到数组
- python 遍历某个路径下包含子目录下的图片名称
- Python爬虫获取POJ某个用户的所有提交状态
- 简单的python爬虫抓取图片实例
- 一个python爬图片的小爬虫
- Python 简单爬虫--获得网上的图片
- python爬虫----简单的图片爬取
- python爬虫爬取好看的图片
- linux ——make
- Javascript类型转换的规则
- 链表求和
- 函数式编程—初识Lambda表达式
- 费马小定理
- 通过Python爬虫爬取知乎某个问题下的图片
- java面试宝典学习笔记(二)
- 数据库查询代价估算优化的深度介绍
- 两个线程运行++a
- xml解析02
- JSP
- spring4.x读书笔记
- 新换的电脑,java 配置环境备忘下
- 过滤器