1.前言

这几天在爬取了美女网站,小说网站之余,想着用scrapy做点逼格高点的爬虫,就想到我们的目前声誉较高的社区——知乎,今天就来爬取知乎的问题及回答,本来知乎是不需要登录就可以爬取,但是首页上推荐的内容就不是你关注的,所以我们需要在登录知乎的前提下(挺重要的技术),爬取我们首页的问题和各个问题的回答

转载请注明

2.知乎登录

知乎登录我之前写过一个关于它登录的文章:知乎登录文章--点击,可以通过这个文章实操一下,在这里就不详说了,这里直接贴出scrapy的知乎登录代码

class ZhihuSpider(scrapy.Spider):name = 'zhihu'allowed_domains = ['www.zhihu.com']start_urls = ['http://www.zhihu.com/']headers = {"HOST": "www.zhihu.com","Referer": "https://www.zhizhu.com",'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36"}def start_requests(self):return [scrapy.Request('https://www.zhihu.com/#signin', headers=self.headers, callback=self.login)]def login(self, response):response_text = response.textmatch_obj = re.match('.*name="_xsrf" value="(.*?)"', response_text, re.DOTALL)xsrf = ''if match_obj:xsrf = (match_obj.group(1))if xsrf:post_url = "https://www.zhihu.com/login/phone_num"post_data = {"_xsrf": xsrf,"phone_num": "你的手机号码","password": "你的知乎登录密码","captcha": ""}import timet = str(int(time.time() * 1000))captcha_url = "https://www.zhihu.com/captcha.gif?r={0}&type=login".format(t)yield scrapy.Request(captcha_url, headers=self.headers, meta={"post_data": post_data},callback=self.login_after_captcha)def login_after_captcha(self, response):with open("captcha.jpg", "wb") as f:f.write(response.body)f.close()from PIL import Imagetry:im = Image.open('captcha.jpg')im.show()im.close()except:passcaptcha = input("输入验证码\n>")post_data = response.meta.get("post_data", {})post_url = "https://www.zhihu.com/login/phone_num"post_data["captcha"] = captchareturn [scrapy.FormRequest(url=post_url,formdata=post_data,headers=self.headers,callback=self.check_login)]def check_login(self,response):text_json = json.loads(response.text)print(text_json)if "msg" in text_json and text_json["msg"] == "登录成功":for url in self.start_urls:yield scrapy.Request(url, dont_filter=True, headers=self.headers)

3.知乎scrapy项目

首先创建scrapy项目 在cmd上cd进入项目目录  输入:scrapy genspider zhihu www.zhihu.com

这样就创建知乎scrapy基础模板:

class ZhihuSpider(scrapy.Spider):name = 'zhihu'allowed_domains = ['www.zhihu.com']start_urls = ['http://www.zhihu.com/']def parse(self, response):pass

4.获取知乎首页所有问题的url
点击一个知乎问题 url为:https://www.zhihu.com/question/50731621/answer/123225450

这是老版的问题url,

你会发现,回答并没有展开,这在我们后面的回答提取上会有困难,这时在浏览器中输入https://www.zhihu.com/question/50731621/answer/123225450即去掉后缀

你会发现回答展开了,所以我们的爬取的问题url为https://www.zhihu.com/question/*******格式的,
代码如下:

def parse(self, response):all_urls = response.css("a::attr(href)").extract()all_urls = [parse.urljoin(response.url, url) for url in all_urls]for url in all_urls:# print(url)match_obj = re.match("(.*zhihu.com/question/(\d+))(/|$).*", url)if match_obj:request_url = match_obj.group(1)yield scrapy.Request(request_url,headers = self.headers,callback=self.parse_question)else:# 如果不是question页面则直接进一步跟踪yield scrapy.Request(url, headers=self.headers, callback=self.parse)

上述代码通过正则表达式取出知乎问题url(新版的,答案展开的)
这里讲callback回调给我们的parse_question即处理问题的函数

5.处理问题函数parse_question的创建
在这个函数中我们要获取问题的详细信息,包括问题的id,title,content,url等,这里我们用到scrapy.loader中的itemloader来加载我们的item,这个itemloader可以直接添加我们的css,xpath,十分方便,但是它默认加载的item格式为list,需要对它重载方法取出str,方便我们载入数据库
这里首先在ArticleSpider.items中重载ItemLoader,并创建ZhihuQuestionItem和
ZhihuAnswerItem(后面会用到)

import re
import scrapy
from scrapy.loader.processors import  TakeFirst,MapCompose,Join
from scrapy.loader import ItemLoaderdef get_nums(value):#将数字的数据提取出来并转换成int类型match_re = re.match(".*?(\d+).*", value)if match_re:nums = int(match_re.group(1))else:nums = 0return numsclass FirstItemLoader(ItemLoader):#自定义itemloader,继承scrapy的ItemLoader类default_output_processor = TakeFirst()class ZhihuQuestionItem(scrapy.Item):# 知乎的问题itemzhihu_id = scrapy.Field()topics = scrapy.Field(input_processor=Join(","))url = scrapy.Field()title = scrapy.Field()content = scrapy.Field()answer_num = scrapy.Field(input_processor=MapCompose(get_nums))comments_num = scrapy.Field()watch_user_num = scrapy.Field()click_num = scrapy.Field()crawl_time = scrapy.Field()
class ZhihuAnswerItem(scrapy.Item):#知乎的问题回答itemzhihu_id = scrapy.Field()url = scrapy.Field()question_id = scrapy.Field()author_id = scrapy.Field()content = scrapy.Field()parise_num = scrapy.Field()
下面创建处理问题的函数
from ArticleSpider.items import ZhihuQuestionItem
from ArticleSpider.items import FirstItemLoader
start_answer_url = "https://www.zhihu.com/api/v4/questions/{0}/answers?include=data%5B*%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cupvoted_followees%3Bdata%5B*%5D.mark_infos%5B*%5D.url%3Bdata%5B*%5D.author.follower_count%2Cbadge%5B%3F(type%3Dbest_answerer)%5D.topics&offset={1}&limit={2}&sort_by=default"def parse_question(self,response):#获取文章idmatch_obj = re.match("(.*zhihu.com/question/(\d+))(/|$).*", response.url)if match_obj:question_id = int(match_obj.group(2))#创建item_loader ,需要传入我们的ZhihuQuestionItemitem_loader = FirstItemLoader(item=ZhihuQuestionItem(), response=response)item_loader.add_css("title", "h1.QuestionHeader-title::text")item_loader.add_css("content", ".QuestionHeader-detail")item_loader.add_value("url", response.url)item_loader.add_value("zhihu_id", question_id)item_loader.add_css("answer_num", ".List-headerText span::text")item_loader.add_css("topics", ".QuestionHeader-topics .Popover div::text")question_item = item_loader.load_item()#print(question_item)yield scrapy.Request(self.start_answer_url.format(question_id,0,20),headers=self.headers,callback=self.parse_answer)yield question_item

上述有个start_answer_url 是知乎请求回答数据的url,可以通过分析网页提交的url得知,传入的参数(question_id,0,20)即初始数据为 0-20,这个函数回调给parse_answer,并将取到的question_item交给pipeline处理

6.处理回答函数parse_answer的创建
这里首先要搞清楚知乎传给我们的数据为什么格式,都有哪些数据,通过在旧版本的知乎链接https://www.zhihu.com/question/50731621/answer/123225450 中按f12
点击查看全部答案  ,可以见到它传回的response

可以看到传回的数据为json格式,在这里还可以看到headers

这里的url就是之前我们说的start_answer_url,接下来贴出代码

def parse_answer(self,response):#返回数据为json格式ans_json = json.loads(response.text)is_end = ans_json["paging"]["is_end"]totals_answer = ans_json["paging"]["totals"]next_url = ans_json["paging"]["next"]#提取answer的具体字段for answer in ans_json["data"]:answer_item = ZhihuAnswerItem()answer_item["zhihu_id"] = answer["id"]answer_item["url"] = answer["url"]answer_item["question_id"] = answer["question"]["id"]answer_item["author_id"] = answer["author"]["id"] if "id" in answer["author"] else Noneanswer_item["content"] = answer["content"] if "content" in answer else Noneanswer_item["parise_num"] = answer["voteup_count"]answer_item["comments_num"] = answer["comment_count"]answer_item["create_time"] = answer["created_time"]answer_item["update_time"] = answer["updated_time"]answer_item["crawl_time"] = datetime.datetime.now()print(answer_item)yield answer_item#提取下一个更多answerif not is_end:yield scrapy.Request(next_url,headers=self.headers,callback=self.parse_answer)
既然是json数据,当然要用json来加载了,加载出来的item就是str类型,十分方便写入数据库

7.在pipelines中将数据写入数据库
首先在setting文件中写入数据库连接信息,还有运行的pipelines(取消注释后加入
MysqlTwistedPipline,后面会说到,是载入mysql的类)

ITEM_PIPELINES = {'ArticleSpider.pipelines.MysqlTwistedPipline': 300,}
MYSQL_HOST = "127.0.0.1"
MYSQL_DBNAME = "article_spider"
MYSQL_USER = "root"
MYSQL_PASSWORD = "root"

这里通过twisted的adbapi模块来加载mysqldb,可以将数据库的写入变成异步的(更快)

import MySQLdb
import MySQLdb.cursors
from twisted.enterprise import adbapi
#异步操作数据库
class MysqlTwistedPipline(object):def __init__(self, dbpool):#把dbpool接收到对象中来,数据连接池self.dbpool = dbpool
#写入setting文件,@classmethoddef from_settings(cls, settings):dbparms = dict(host = settings["MYSQL_HOST"],db = settings["MYSQL_DBNAME"],user = settings["MYSQL_USER"],passwd = settings["MYSQL_PASSWORD"],charset='utf8',cursorclass=MySQLdb.cursors.DictCursor,use_unicode=True,)#用mysqldb模块,传入连接数据,首先将参数变成dict,传入时加**为可变长度变量dbpool = adbapi.ConnectionPool("MySQLdb", **dbparms)return cls(dbpool)def process_item(self, item, spider):#使用twisted将mysql插入变成异步执行query = self.dbpool.runInteraction(self.do_insert, item)query.addErrback(self.handle_error) #处理异常def handle_error(self, failure):#处理异步插入的异常print (failure)def do_insert(self, cursor, item):#执行具体的插入#根据不同的item 构建不同的sql语句并插入到mysql中#知乎问题插入mysqlif item.__class__.__name__ == "ZhihuQuestionItem":insert_sql = """insert into zhihu_question(zhihu_id, topics, url, title, content, answer_num)VALUES (%s, %s, %s, %s, %s, %s)  """cursor.execute(insert_sql, (item["zhihu_id"], item["topics"],item["url"],item["title"], item["content"],item["answer_num"]))#知乎回答插入if item.__class__.__name__ == "ZhihuAnswerItem":insert_sql = """insert into zhihu_answer(zhihu_id, question_id, url, parise_num, content, author_id)VALUES (%s, %s, %s, %s, %s, %s)"""cursor.execute(insert_sql, (item["zhihu_id"], item["question_id"], item["url"], item["parise_num"], item["content"], item["author_id"]))

注意:这里的mysqldb在python3中为mysqlclient

8.在navicat中创建数据库和表

首先创建数据库 article_spider

接下来创建问题表

接下来创建回答表

这样就大功告成啦

9.运行
创建main.py,写入

from scrapy.cmdline import executeimport sys
import osprint(os.path.abspath(__file__))
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
execute(["scrapy","crawl","zhihu"])

这样运行main.py就能运行

运行结果:

10.后续
  这次只是简单的实现知乎的爬取,因为知乎的反爬机制比较成熟,所以常常爬到十几条数据它就会弹出验证码,这样我们的爬虫就无法爬取网站信息,这在后面我会使用自动输入验证码的方法,目前代码比较简陋希望各位亲别介意
  此外这次代码量有点大,如果照上面的步骤不能实现的,可以到我的github上下载完整的代码:https://github.com/oldbig-carry/jobbole_spider   这个与之前伯乐在线的代码集合在一起了,可以的话,给个star哦

转载于:https://my.oschina.net/u/1987703/blog/1617546

scrapy初始第二波——爬取知乎首页的问题和回答并写入mysql中相关推荐

  1. Scrapy实战:爬取知乎用户信息

    思路:从一个用户(本例为"张佳玮")出发,来爬取其粉丝,进而爬取其粉丝的粉丝- 先来观察网页结构: 审查元素: 可以看到用户"关注的人"等信息在网页中用json ...

  2. 爬取知网博硕士文献及中国专利存到mysql数据库中的代码及其注意事项

    今天因为需要做了一个爬取知网博硕士论文及中国专利的爬虫,在制作的过程中遇到了不少坑,在网上查资料时都是很老的资源,在现在知网的反爬虫下不起作用,所以我来写这篇文章来供大家参考.(这篇文章主要介绍通过改 ...

  3. python抽取指定url页面的title_Python使用scrapy爬虫,爬取今日头条首页推荐新闻

    爬取今日头条https://www.toutiao.com/首页推荐的新闻,打开网址得到如下界面 查看源代码你会发现 全是js代码,说明今日头条的内容是通过js动态生成的. 用火狐浏览器F12查看得知 ...

  4. scrapy初始第一波——爬取伯乐在线所有文章

    1 前言    要说到爬虫界的明星,当属我们的python,而这得益于我们的爬虫明星框架--scrapy,这就让我们不得不学习它,这几天刚好用它做一些小demo,就将其总结一下,希望能对大家的学习爬虫 ...

  5. python爬取知乎问题_python爬取知乎首页问题

    我的代码如下:importurllib.requestimporthttp.cookiejarurl_a="https://www.zhihu.com/"url_a="h ...

  6. 平常人可以漂亮到什么程度?教你爬取知乎大神们的回答一探究竟!

    大家好,今天才哥带大家看看知乎这个高达14.3万关注,2.6亿浏览,回答数超过1.27万的问题<平常人可以漂亮到什么程度?>. 最近呢,可能是因为写了几篇关于爬虫获取美女照片的文章的缘故? ...

  7. python爬取丁香园首页疫情json数据,尝试存入mysql数据库

    新手练python爬虫 # -*- coding:utf-8 -*- """ 作者:孙敏 日期:2022年01月01日 """ import ...

  8. python xpath爬取新闻标题_爬取知乎热榜标题和连接 (python,requests,xpath)

    用python爬取知乎的热榜,获取标题和链接. 环境和方法:ubantu16.04.python3.requests.xpath 1.用浏览器打开知乎,并登录 2.获取cookie和User-Agen ...

  9. 以爬取知乎为例,进行python 多进程爬虫性能分析

    以爬取知乎为例,进行python 多进程爬虫性能分析 如果对多进程multiproessing模块不熟悉,请先浏览 python 使用multiprocessing模块进行多进程爬虫 问题背景: 爬取 ...

最新文章

  1. 【linux】Valgrind工具集详解(十四):Cachegrind(缓存和分支预测分析器)
  2. JavaScript实时更新中国标准时间
  3. 《Zabbix安装部署-1》-Centos7
  4. vue前端上传文件夹的插件_基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件...
  5. 花小猪,真正的对手是谁?
  6. Linux中find命令详解
  7. JAVA中如何取map的值_如何在java中取map中的键值 的两种方法
  8. 高光谱遥感数据集下载及简介
  9. jquery 判断checkbox是否为空的三种方法
  10. 【原】Storm调度器
  11. 角度传感器原理和应用
  12. 过采样之SMOTE算法
  13. Delphi 字体修改一例 (转)
  14. android 方法映射,高通Android平台驱动层 MSM8916 键值映射方法
  15. C#服务端的微信小游戏——多人在线角色扮演(六)
  16. php开发中常用字符串函数总结
  17. 种子计数法对种子公司的好处
  18. ArcGIS for IOS 添加多个覆盖物,并设置点击覆盖物触发委托
  19. 无为无欲、与世无争,也就没有烦恼......
  20. 价格为3000元的计算机的机型,2000-3000元左右的笔记本电脑推荐,性价比超高

热门文章

  1. 软件测试面试题集(含答案)
  2. 4.28 poll API介绍及代码编写
  3. 【运维笔记】两台主机上的两台vmware虚拟机实现互相连通组网
  4. 基于机器视觉的图像灰度化方法比较分析
  5. 猪与蟒(pig and python)
  6. MySQL5.6数据库多种安装方法及企业级安装实践
  7. 像素鸟 游戏开发教程 FlappyBird Game development tutorial
  8. 用Notes打造一个资讯中心
  9. 神经网络算法可以用来干什么
  10. 大战Mysql2003