python知乎内容抓取(redis存储)

  因为平时喜欢上知乎,发现其话题是一个有向无环图(自己介绍说得),一级一级往上最后到根话题,所以我就想尝试从根话题一级一级往下将其全部内容爬取。最后实践过程中发现自己想多了..有以下三个问题:

  1.数据量巨大,单台电脑能力肯定不够。我这里只抓取了话题结构和话题对应的单个页面(不翻页)的一些呈现信息,没有抓取具体问题回答的内容。

  2.如果直接连续抓取,没多少次就会被知乎检测到异常,这里使用代理ip可以解决。

  3.最麻烦的是要查看所有话题结构必须要登录(如下图示例显示的子话题是可以抓到的,但是点击查看全部话题结构就需要登录),一旦登录就不可能连续抓取,除非能搞到很多账号,而且还需要破解验证等问题。我这块目前只是从根话题抓取直接显示出来的子话题,所以抓取到的话题数量和实际差的很远(只是练习参考..)。后面我想了一个其他模式抓取,实际如果试验可以会重新搞一套。

 

  存储由于key-value型很适合这里,所以我使用的是redis数据库,而且很快。一共有四张key-value表,zhTopicName保存话题名称(key)-子话题id(value,后同),zhTopicTree保存话题id-[子话题id...],zhTopicMessage保存话题id-(关注人数,问题数),zhTopicQuestions保存话题id-[(问题名称,作者,评论数,点赞数,链接)...]

参考代码如下:

  1 # -*- coding: UTF-8 -*-
  2
  3 from bs4 import BeautifulSoup
  4 import requests
  5 import random
  6 import redis
  7 import time
  8 import json
  9
 10 # 子话题目录id获取接口url,当前初始根话题id为19776749
 11 CHILDREN_URL = ‘https://www.zhihu.com/api/v3/topics/%u/children‘
 12
 13 # 知乎精华url
 14 TOP_ANSWERS_URL = ‘https://www.zhihu.com/topic/%u/top-answers‘
 15
 16 # 请求头
 17 HEADERS = {‘User-Agent‘: ‘Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36  18  16 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36‘}
 19
 20 # 代理ip,这里示例放了一个,自己可以在网上找些补充下
 21 PROXIES_LIST = [
 22     {"http": "218.14.227.198:3124"},
 23 ]
 24
 25 pool = redis.ConnectionPool(host=‘localhost‘, port=6379, db=0)
 26
 27 r = redis.Redis(connection_pool=pool)
 28
 29 s = requests.session()
 30
 31
 32 def crawl_message(uid):
 33     try:
 34         req = s.get(url=TOP_ANSWERS_URL % int(uid), headers=HEADERS, proxies=random.choice(PROXIES_LIST)).text
 35     except Exception as e:
 36         raise e
 37     bf = BeautifulSoup(req, ‘html.parser‘)
 38     strong = bf.find_all(‘strong‘, class_=‘NumberBoard-itemValue‘)
 39     # 0为关注人数,1为问题数
 40     try:
 41         r.hset(‘zhTopicMessage‘, uid, (strong[0].get(‘title‘), strong[1].get(‘title‘)))
 42     except Exception as ex:
 43         print(uid, ex)
 44         return
 45     items = bf.find_all(‘div‘, class_=‘ContentItem AnswerItem‘)
 46     tqs = []
 47     for t in items:
 48         # 问题、作者、评论数、点赞数、链接
 49         tqs.append((eval(t.get(‘data-zop‘))[‘title‘], eval(t.get(‘data-zop‘))[‘authorName‘],
 50                     t.find(itemprop=‘commentCount‘).get(‘content‘),
 51                     t.find(‘button‘, class_="Button VoteButton VoteButton--up").contents[-1],
 52                     t.find(‘a‘).get(‘href‘)))
 53     r.hset(‘zhTopicQuestions‘, uid, tqs)
 54     time.sleep(0.1)
 55
 56
 57 # 获取话题的那个有向无环图以及其名称与id的对应关系并入库
 58 def crawl_topic():
 59     while len(crawl_topic.zhTopic) > 0:
 60         try:
 61             data =  62                 json.loads(
 63                     s.get(url=CHILDREN_URL % int(crawl_topic.zhTopic[0]), headers=HEADERS,
 64                           proxies=random.choice(PROXIES_LIST)).text)[
 65                     ‘data‘]
 66         except Exception as ex:
 67             raise ex
 68         if not data:  # 已经到叶子话题返回
 69             crawl_topic.zhTopic.pop(0)
 70             continue
 71         ids = []
 72         for m in data:
 73             nid = m[‘id‘]
 74             ids.append(nid)
 75             crawl_topic.zhTopic.append(nid)
 76             r.hset(‘zhTopicName‘, m[‘name‘], nid)
 77         r.hset(‘zhTopicTree‘, nid, ids)
 78         crawl_topic.zhTopic.pop(0)
 79
 80
 81 def start_crawl():
 82     # 19776749为当前知乎根话题id
 83     # 这里构造一个id队列避免使用递归
 84     crawl_topic.zhTopic = [19776749]
 85     r.hset(‘zhTopicName‘, ‘「根话题」‘, 19776749)
 86     crawl_topic()
 87     ids = r.hvals(‘zhTopicName‘)
 88     for tid in ids:
 89         crawl_message(tid)
 90
 91
 92 def result_display():
 93     try:
 94         for tnm in r.hkeys(‘zhTopicName‘):
 95             tid = r.hget(‘zhTopicName‘, tnm)
 96             print(‘话题:‘, tnm.decode(), ‘    关注人数:‘, tid[1], ‘    问题数:‘, tid[0])
 97             for e in eval(r.hget(‘zhTopicQuestions‘, tid).decode()):
 98                 print(‘    ‘, ‘问题:‘, e[0], ‘作者:‘, e[1], ‘评论数‘, e[2], ‘点赞数:‘, e[3], ‘链接:‘, e[4])
 99     except Exception as ex:
100         print(ex)
101
102
103 if __name__ == ‘__main__‘:
104     start_crawl()
105     result_display()

  在我本机测试一共运行了5个小时左右获取了1万3千多个话题(如上问题3所述,远少于实际话题数,估计不到十分之一),每个话题不到5条精华问题(页面直接展示几条就抓几条,没有翻页)。下面展示下运行结果:

  最后按网上流行那种话题关注人数多少对应字体大小画张图展示下(我也是现学现卖,还是那句话仅供参考:))

代码如下:

 1 import matplotlib.pyplot as plt
 2 from matplotlib import colors as mcolors
 3 import numpy as np
 4 import random
 5 import redis
 6
 7 pool = redis.ConnectionPool(host=‘localhost‘, port=6379, db=0)
 8
 9 r = redis.Redis(connection_pool=pool)
10
11 followers = []
12 for tnm in r.hkeys(‘zhTopicName‘):
13     tid = r.hget(‘zhTopicName‘, tnm)
14     msg = r.hget(‘zhTopicMessage‘, tid)
15     if msg:
16         followers.append((tnm.decode(), int(eval(msg)[0])))
17 # 按关注人数取前300的话题
18 hotTopics = sorted(followers, key=lambda follower: follower[1], reverse=True)[0:300]
19
20 plt.figure(figsize=(10, 6.18))
21 plt.setp(plt.gca(), frame_on=False, xticks=(), yticks=())
22 plt.axis([0, 100, 0, 100])
23 plt.rcParams[‘font.sans-serif‘] = [‘SimHei‘]
24
25 randC = np.linspace(-20, 100, 300)
26
27 colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
28 by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgba(color)[:3])), name)
29                 for name, color in colors.items())
30 sorted_names = [name for hsv, name in by_hsv]
31 random.shuffle(sorted_names)
32
33 random.shuffle(randC)
34
35 rankings = 0
36 for topic in hotTopics:
37     plt.text(randC[rankings], randC[299 - rankings], topic[0], fontsize=(300 - rankings) / 10 + 10,
38              rotation=random.randint(-30, 30), style=‘italic‘, alpha=0.75,
39              color=sorted_names[rankings % len(sorted_names)])
40     rankings = rankings + 1
41
42 plt.show()

运行结果:

原文地址:https://www.cnblogs.com/prophet-ss/p/9416129.html

时间: 2024-10-11 07:44:12

python知乎内容抓取(redis存储)的相关文章

python&php数据抓取、爬虫分析与中介,有网址案例

近期在做一个网络爬虫程序.后台使用python不定时去抓取数据.前台使用php进行展示 站点是:http://se.dianfenxiang.com

c# 正则表达式对网页进行内容抓取

原文 c# 正则表达式对网页进行内容抓取 搜索引擎中一个比较重要的环节就是从网页中抽取出有效内容.简单来说,就是吧HTML文本中的HTML标记去掉,留下我们用IE等浏览器打开HTML文档看到的部分(我们这里不考虑图片). 将HTML文本中的标记分为:注释,script ,style,以及其他标记分别去掉: 1.去注释,正则为: output = Regex.Replace(input, @"<!--[^-]*-->", string.Empty, RegexOptions.

Java实现web页面内容抓取

1 package demo; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 8 /** 9 * web页面内容抓取 10 * @author sy 11 * 12 */ 13 public class GrabWebHtml{ 14 15 public static void

python爬虫CSDN文章抓取

CSDN原则上不让非人浏览访问,正常爬虫无法从这里爬取文章,需要进行模拟人为浏览器访问. 使用:输入带文章的CSDN链接自动生成正文的HTML,文件名为标题名 #!/usr/bin/env python # coding=utf-8 ######################################### #> File Name: CSDN_article.py #> Author: nealgavin #> Mail: [email protected] #> Cre

分享一个简单的python+mysql网络数据抓取

最近学习python网络爬虫,所以自己写了一个简单的程序练练手(呵呵..).我使用的环境是python3.6和mysql8.0,抓取目标网站为百度热点(http://top.baidu.com/).我只抓取了实时热点内容,其他栏目应该类似.代码中有两个变量SECONDS_PER_CRAWL和CRAWL_PER_UPDATE_TO_DB,前者为抓取频率,后者为抓取多少次写一次数据库,可自由设置.我抓取的数据内容是热点信息,链接,关注人数和时间.其在内存中存放的结构为dict{tuple(热点信息,

有了 Docker,用 JavaScript 框架开发的 Web 站点也能很好地支持网络爬虫的内容抓取

点这里 阅读目录 用 AngularJS(以及其它 JavaScript 框架)开发的 Web 站点不支持爬虫的抓取 解决方案 为什么公开我们的解决方案 实现 AngularJS 服务 结论 Prerender 服务能够为网络爬虫提供预先渲染的动态页面内容,解决了用 JavaScript 框架构建的 Web 站点不支持爬虫抓取的问题.本文详细描述了一种解决方案,尤其是提供了集成 Prerender 服务的 Docker 容器镜像. 如果你正在使用 AngularJS 构建一个面向大众消费者的应用

python的post请求抓取数据

python通过get方式,post方式发送http请求和接收http响应-urllib urllib2 python通过get方式,post方式发送http请求和接收http响应-- import urllib模块,urllib2模块, httplib模块 http://blog.163.com/[email protected]/blog/static/132229655201231085444250/ 测试用CGI,名字为test.py,放在apache的cgi-bin目录下:#!/usr

python抓取数据,python使用socks代理抓取数据

在python中,正常的抓取数据直接使用urllib2 这个模块: import urllib2 url = 'http://fanyi.baidu.com/' stream = urllib2.urlopen(url) cont = stream.read() print cont 如果要走http代理的话,我们也可以使用urllib2,不需要引用别的模块: import urllib2 url = 'https://clients5.google.com/pagead/drt/dn/dn.j

Python开发网络爬虫抓取某同城房价信息

前言: 苦逼的我从某某城市换到另一个稍微大点的某某城市,面临的第一个问题就是买房,奋斗10多年,又回到起点,废话就不多说了,看看如何设计程序把某同城上的房价数据抓取过来. 方案:方案思路很简单,先把网页内容获取下来,通过一定规则对内容解析,保存成想要的格式 难点是对网页的解析,是一个比较细致的活,必须边输出,边调试. 具体实现: 获取网页内容: def get_page(url):    headers = {        'User-Agent': r'Mozilla/5.0 (Window