Scrapy抓取知乎

image.png

今天给大家带来如何抓取知乎网站中最新热点栏目中的信息,获取里面的标题、内容、作者、网友评论、点赞量等信息。获取这些数据可以提取我们想要的内容进行数据分析和数据展示,建立一个自己的网站,将获取的内容进行展示!

1.软件安装scrapy+selenium+chrome(详情见我的上一篇文章,这里就不提了)

2.接下来我就直接上代码,并进行一定的详解

1)首先要抓取知乎的数据我们需要进行模拟登陆后,获取cookie并保存到mongo数据库 如下:
# -*- coding: utf-8 -*-

import sys
import time
import pymongo
from selenium.webdriver import DesiredCapabilities

reload(sys)
sys.setdefaultencoding("utf-8")

if __name__ == '__main__':
    # 连接mongo创建数据库和表为后面保存cookie做准备 
    client = pymongo.MongoClient(host="mongodb://192.168.98.5:27017")
    dbs = client["zhihu"]
    table = dbs["cookies"]
    from selenium import webdriver
    # browser = webdriver.Chrome()
    # 加载chromeoptions 是一个方便控制 chrome 启动时属性的类
    option = webdriver.ChromeOptions()
    # 无头模式启动
    option.add_argument("--headless")
    # 谷歌文档提到需要加上这个属性来规避bug
    option.add_argument("--disable-gpu")
    # 取消沙盒模式
    option.add_argument("--no-sanbox")
    # 单进程运行
    option.add_argument("--single-process")
    # 设置网页大小
    option.add_argument("--window-size=414,736")
    # 添加useragent
    option.add_argument("user-agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'") 
    browser = webdriver.Chrome(chrome_options=option)
    try:
        browser.get("https://www.zhihu.com/signin")
        browser.find_element_by_css_selector(".SignFlow-accountInput.Input-wrapper input").send_keys(
            "你的知乎账号")
        time.sleep(1)
        browser.find_element_by_css_selector(".SignFlow-password input").send_keys(
            "你的知乎密码")
        time.sleep(2)
        browser.find_element_by_css_selector(
            ".Button.SignFlow-submitButton").click()
        time.sleep(3)
        zhihu_cookies = browser.get_cookies()
        cookie_dict = {}
        for cookie in zhihu_cookies:
            cookie_dict[cookie['name']] = cookie['value']
        table.insert(cookie_dict)
        print "插入成功"
        browser.close()
    except Exception, e:
        zhihu_cookies = browser.get_cookies()
        cookie_dict = {}
        for cookie in zhihu_cookies:
            cookie_dict[cookie['name']] = cookie['value']
        print cookie_dict
        browser.close()
        print e

ChromeOptions是Chrome 的一个类,她的作用是在启动chrome的时候进行一定的设置。如添加参数,阻止图片加载,阻止JavaScript执行 等动作。这些需要 selenium的 ChromeOptions 来帮助我们完成。
以上代码就是连接到mongo,创建好数据库和数据表,并用selenium和chrome结合以浏览器的行为去访问知乎网站,找到输入框,输入账号和密码获取到cookie。并进行提取存到mongo指定的数据库中,供后面的爬虫在发送请求的时候去访问和携带!

2)配置settings,设置延时,配置mongo 等
ROBOTSTXT_OBEY = False
LOG_LEVEL = "WARNING"
MONGO_URI = 'mongodb://xxx.xxx.xx.x:27017'
MONGODB_DBNAME = 'zhihu'
MONGODB_DBTABLE = 'zh_data'
MONGODB_COOKIE = 'cookies'
DOWNLOAD_DELAY = 0.8
3) 爬虫spider核心代码详解
# -*- coding: utf-8 -*-
import json
import os
import re
from datetime import datetime
from os import path
from urlparse import urljoin
import pymongo
import scrapy
import sys
from bs4 import BeautifulSoup
from copy import deepcopy
from selenium.webdriver import DesiredCapabilities
from zhihu import settings
reload(sys)
sys.setdefaultencoding('utf8')
from scrapy.loader import ItemLoader

class ZhihuSpider(scrapy.Spider):
    name = "zh"
    allowed_domains = ["www.zhihu.com"]
    start_urls = ['https://www.zhihu.com/']
    # question的第一页answer的请求url
    start_url = "https://www.zhihu.com/hot"
    question_detail_url = "https://www.zhihu.com/api/v4/questions/{0}/answers?include=data%5B%2A%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%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=5&offset={1}&sort_by=default"
    q_detail_url = "https://www.zhihu.com/api/v4/articles/{0}/comments?include=data%5B*%5D.author%2Ccollapsed%2Creply_to_author%2Cdisliked%2Ccontent%2Cvoting%2Cvote_count%2Cis_parent_author%2Cis_author%2Calgorithm_right&order=normal&limit=20&offset=0&status=open"

    headers = {
        "HOST": "www.zhihu.com",
        "Referer": "https://www.zhizhu.com",
        'User-Agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/68.0.3440.106 Safari/537.36"
    }

    def __init__(self, param=None, *args, **kwargs):
        super(ZhihuSpider, self).__init__(*args, **kwargs)
        client = pymongo.MongoClient(host=settings.MONGO_URI)
        dbs = client[settings.MONGODB_DBNAME]
        self.table = dbs[settings.MONGODB_DBTABLE]

    def parse(self, response):
        section_list = response.xpath("//section[@class='HotItem']")
        for section in section_list:
            url = section.xpath(".//div[@class='HotItem-content']/a/@href").extract_first()
            title = section.xpath(".//div[@class='HotItem-content']/a/@title").extract_first()
            question_id = url.split("/")[-1]
            if "question" in url:
                detail_url = self.question_detail_url.format(question_id, 5)
                yield scrapy.Request(
                    detail_url,
                    callback=self.parse_detail,
                    meta={"meta": deepcopy(url)}
                )
            else:
                detail_url = self.q_detail_url.format(question_id)
                yield scrapy.Request(
                    detail_url,
                    callback=self.q_parse_detail,
                    meta={"url": deepcopy(url), "title": title}
                )

    def parse_detail(self, response):
        question_url = response.meta["meta"]
        detail_url = response.url
        all_dict = json.loads(response.text)
        data_dict = all_dict["data"]

        for data in data_dict:
            item1 = {}
            item1["question_url"] = question_url
            item1["title"] = data["question"]["title"]
            item1["content"] = data["content"]
            item1["comment_count"] = data["comment_count"]
            item1["voteup_count"] = data["voteup_count"]
            p2 = re.compile(u'[^\u4e00-\u9fa5]')  # 中文的编码范围是:\u4e00到\u9fa5
            item1["content"] = p2.sub(r'', item1["content"])
            print "===========>question_url:{0}".format(question_url)
            print "===========>title:{0}".format(item1["title"])
            print "===========>点赞量:{0}".format(item1["voteup_count"])
            print "===========>评论量:{0}".format(item1["comment_count"])
            print "===========>评论:{0}".format(item1["content"])
            #self.table.insert(item1)
        paging = all_dict["paging"]
        if not paging["is_end"]:
            next_url = paging["next"]
            yield scrapy.Request(
                next_url,
                self.parse_detail,
                meta={"meta": deepcopy(question_url)}
            )

    def q_parse_detail(self, response):
        question_url = response.meta["url"]
        title = response.meta["title"]
        detail_url = response.url
        all_dict = json.loads(response.text)
        data_dict = all_dict["data"]

        for data in data_dict:
            content = data["content"]
            comment_count = 0
            vote_count = data["vote_count"]
            p2 = re.compile(u'[^\u4e00-\u9fa5]')  # 中文的编码范围是:\u4e00到\u9fa5
            content = p2.sub(r'', content)
            item2 = {}
            item2["question_url"] = question_url
            item2["title"] = title
            item2["voteup_count"] = vote_count
            item2["comment_count"] = comment_count
            item2["content"] = content
            print "===========>question_url:{0}".format(question_url)
            print "===========>title:{0}".format(title)
            print "===========>点赞量:{0}".format(vote_count)
            print "===========>评论量:{0}".format(comment_count)
            print "===========>评论:{0}".format(content)
            #self.table.insert(item2)

        paging = all_dict["paging"]
        if not paging["is_end"]:
            next_url = paging["next"]
            yield scrapy.Request(
                next_url,
                self.q_parse_detail,
                meta={"url": deepcopy(question_url), "title": deepcopy(title)}
            )
        # pass

    def start_requests(self):
        return [scrapy.Request(url=self.start_url, dont_filter=True, cookies=cookie_dict, headers=self.headers)]

以上代码很简单,scrapy拿到的第一个请求,也就是start_urls,引擎会从spider中拿到https://www.zhihu.com/hot这个请求交给调度器去入队列,执行调度。调度器将封装好的请求返回给引擎,引擎会将刚刚处理过得请求交给下载器去下载,下载器在下载中间件中可以对该请求添加cookie、useragent、代理等方式,并将下载好的response返回给spider中的parse,将产生新的url反复执行如上。
爬虫开始抓取数据,默认是直接访问知乎的热点栏目https://www.zhihu.com/hot,查看parse函数中代码。首先获取到热点栏目下的超链接,然后从超链接中提取出每一个作者的id号,供后面抓取每一个超链接下的评论、点赞等使用。需要注意的是这里会有两种类型的超链接,一种是提问的超链接,另一种是热点的描述。分别做处理后,去请求这些超链接。question_detail_url、q_detail_url这两个类变量是两个初始的json url。
每一个超链接下面都是json加载的数据,你往下拉的时候会发现一直刷新,这个时候我们使用google浏览器中的调试工具去捕捉,你会发现network中xhr里能够看到这样的信息

image.png

其实只要知道这个url,当你去访问它,它里面的内容就会提示你去访问新的url,你只需要提取去访问就ok。附上json中数据:
{"paging":{"is_end":true,"is_start":true,"next":"https://www.zhihu.com/answers/516374549/concerned_upvoters?limit=10\u0026offset=10","previous":"https://www.zhihu.com/answers/516374549/concerned_upvoters?limit=10\u0026offset=0","totals":0},"data":[]}

这条json数据中,告诉我们下一个url和上一个url的路径,以及是否是最后一页,我们可以根据json中的next这个field去访问url,当is_end这个field为false即表示数据已经提取完成。提取规则写完了后,大家可以将数据保存到指定的数据库即可,爬虫到此就结束了,谢谢大家!欢迎大家提问。

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335