初用Python爬贴吧——他们的粉丝至少有多少水分?

前言

百度贴吧有十余年历史,笔者对此也有五年左右的持续接触。目睹了旧版贴吧被更易于插入广告的新版贴吧所取代,粗暴的帖间广告转变成真假莫辨的软文攻势,随之而来的两层会员制度,无孔不入的营销推广,甚至打破道德底线的变现行径……出于对贴吧现状的好奇,刚刚接触了Python爬虫的笔者随机抽取了15名大型贴吧大吧主各自的一部分粉丝进行粗略的二分,以期估计他们的粉丝中至少有多大比例的用户为僵尸粉丝

平台及工具

    Windows 10
    Mozillia Firefox Web Browser 52.0.2 (32-bit)
    Python 3.6 (32-bit)
            bs4(BeautifulSoup分析网页内容供搜索)
            urllib(URL相关核心模块)
            openpyxl(将数据保存为Excel表格)
            time(等待)
            selenium(网页元素互动,用于登录一个获取他人个人页面的用户)

            numpy pandas(用户信息矩阵建立,该版本中暂无用)

数据收集

** input:**用于获取粉丝的根节点ID列表
** output:**每个根节点至少全部,至多300名粉丝的信息(ID/吧龄/发帖数/关注/粉丝),是否为僵尸ID的判断结果,僵尸ID的比例。
    暂定的僵尸粉丝判定条件为:
            1.粉丝10人以下,关注100人以上的。or
            2.粉丝10人以上,关注在粉丝的10倍以上的。or
            3.个人主页无法正常访问返回404页面的。

注:满足条件3的用户大部分是贴吧自动封禁的僵尸、违禁内容发射装置,因此也划入此类。另外,存在一些僵尸ID形成了互相关注的僵尸网络,此时也无法算计在内,需要在以后的更新中加深爬取度数并更深入的分析,此为以后的计划。为此,代码亦收集了用户吧龄、发帖数等信息,不影响本次运作的结果,仅作保存。

页面分析



如果要进行大于1度的搜索,只需要在用户队列中继续讲1度用户作为“根节点”,将他们的粉丝加入用户队列即可(暂未用到)。
自动登录指定账号作为搜集者——>
根(0度)用户个人信息搜集——>根(0度)用户粉丝ID搜集——>
粉丝(1度)用户个人信息搜集——>粉丝(1度)用户粉丝ID搜集(——>
2度用户个人信息搜集——>2度用户粉丝ID搜集——>>N度用户)

代码实现

# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
import urllib.request
from urllib.request import urlopen
import urllib.parse
import numpy as np
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import time
import openpyxl

class browser:

def __init__(self):
    self.br = webdriver.Firefox()
    self.login()

def login(self):
    self.br.get("http://tieba.baidu.com")
    log = self.br.find_element_by_xpath('//a[@class="btn_login"]')
    log.click()
    time.sleep(5.0)
    username = self.br.find_element_by_id('TANGRAM__PSP_8__userName')
    password = self.br.find_element_by_id("TANGRAM__PSP_8__password")
    username.clear()
    password.clear()
    username.send_keys("搜寻者账号")
    password.send_keys("搜寻者密码")
    self.br.find_element_by_id("TANGRAM__PSP_8__submit").click()

def get(self, url):
    self.br.get(url)
    return self.br.page_source

class Person:

def __init__(self, username, degree):
    # fans,concerns,age,mes,uid
    self.username = username
    self.fans = '0'
    self.concerns = '0'
    self.age = '0'
    self.mes = '0'
    self.degree = degree
    self.uid = "未查到(无关注边)"
    self.fan = []
    self.concern = []

def select(self):
    try:
        url = "http://tieba.baidu.com/home/main/?un={}".format(
            urllib.parse.quote(self.username))
    except:
        return
    try:
        soup = BeautifulSoup(
            urlopen(url).read().decode('utf-8'), "html.parser")
    except:
        return
    self.age, _, self.mes = soup.find(
        'div', {'class': 'userinfo_userdata'}).findAll('span')[1:4]
    self.age = self.age.string.split(':')[1][:-1]
    self.mes = self.mes.string.split(':')[1]
    if self.mes[-1:] == '万':
        self.mes = str(int(float(self.mes[:-1]) * 10000 + 500))
    partall = soup.findAll('h1', {'class': 'ihome_aside_title'})
    allsum = soup.findAll('span', {'class': 'concern_num'})
    ind = 0
    for count, name in enumerate(partall):
        name = str(name)
        name = name.split('>')[1][:5]
        if name == "他关注的人" or name == "她关注的人":
            self.concerns = allsum[ind].find('a').string
            ind += 1
        if name == "关注他的人" or name == "关注她的人":
            self.fans = allsum[ind].find('a').string
    try:
        self.uid = soup.find('span', {'class': 'concern_num'}).find(
            'a').get('href').split('id=')[1].split('?')[0]
    except:
        pass
    self.fr = pd.DataFrame(
        [[self.username, self.fans, self.concerns, self.age, self.mes, self.degree, '', '', '', '']], columns=ids)
    done.add(self.username)

def getfans(self):
    if self.uid[0] == '未':
        return
    for page in range(1, 16):
        url = "http://tieba.baidu.com/i/i/fans?u={}&pn={}".format(
            self.uid, str(page))
        soup = BeautifulSoup(br.get(url), "lxml")
        userlist = soup.findAll('div', {'class': 'user'})
        if len(userlist) == 0:
            break
        for user in userlist:
            name = user.find('span', {'class': 'name'}).find('a').string
            self.fan.append(name)
            if name not in f:
                f[name] = len(persons)
                print("{}度用户<{}>加入成功!".format(str(self.degree + 1), name))
                persons.append(Person(name, self.degree + 1))

def getconcerns(self):
    if self.uid[0] == '未':
        return
    for page in range(1, 16):
        url = "http://tieba.baidu.com/i/i/concern?u={}&pn={}".format(
            self.uid, str(page))
        soup = BeautifulSoup(br.get(url), "lxml")
        userlist = soup.findAll('div', {'class': 'user'})
        if len(userlist) == 0:
            break
        for user in userlist:
            name = user.find('span', {'class': 'name'}).find('a').string
            self.concern.append(name)
            if name not in f:
                f[name] = len(persons)
                print("{}度用户<{}>加入成功!".format(str(self.degree + 1), name))
                persons.append(Person(name, self.degree + 1))

def describe(self):

    print("No:", allUserCounter)
    print("Junk:", junkUserCounter)
    if allUserCounter > 0:
        print("junkPercent:", 100 * junkUserCounter / allUserCounter, "%")
    print("用户名:", self.username)
    print("粉丝数:", self.fans)
    print("关注数:", self.concerns)
    print("吧 龄 :", self.age)
    print("发帖数:", self.mes)

def selectall(self):
    self.select()
    self.getfans()
    self.getconcerns()

wb = openpyxl.load_workbook('res.xlsx')
sheet = wb.active
ids = ["Username", "Fans", "Concerns", "Age", "Messages", "Degree", "isJunkId", "totalId", "junkId", "junkPercent"]
for count, tag in enumerate(ids):
    sheet.cell(row=1, column=count + 1).value = tag
persons = []
allUserCounter = 0
junkUserCounter = 0
f = {}
done = set()
br = browser()
time.sleep(0.3)
initFile = open('0degree.txt', 'r') #读取根用户ID
text = [i for i in initFile.read().split('\n') if i != '']
initFile.close()
for user in text:
    p = Person(user, 0)
    p.selectall()
    sheet.cell(row=sheet.max_row + 1, column=1).value = p.username
    sheet.cell(row=sheet.max_row, column=2).value = int(p.fans)
    sheet.cell(row=sheet.max_row, column=3).value = int(p.concerns)
    sheet.cell(row=sheet.max_row, column=4).value = float(p.age)
    sheet.cell(row=sheet.max_row, column=5).value = int(p.mes)
    sheet.cell(row=sheet.max_row, column=6).value = p.degree
    p.describe()
for d2user in persons:
    if d2user.username not in done:
        allUserCounter += 1
        p = Person(d2user.username, 1)
        p.select()
        print("Accecss!!!--->" + d2user.username)
        sheet.cell(row=sheet.max_row + 1, column=1).value = p.username
        sheet.cell(row=sheet.max_row, column=2).value = int(p.fans)
        sheet.cell(row=sheet.max_row, column=3).value = int(p.concerns)
        sheet.cell(row=sheet.max_row, column=4).value = float(p.age)
        sheet.cell(row=sheet.max_row, column=5).value = int(p.mes)
        sheet.cell(row=sheet.max_row, column=6).value = p.degree
        if int(p.concerns) >= 100 and int(p.fans) <= 10 or (int(p.fans) >= 10 and int(p.concerns) / int(p.fans) >= 10) or int(p.concerns) + int(p.fans) == 0:
            junkUserCounter += 1
            sheet.cell(row=sheet.max_row, column=7).value = 1
        p.describe()
    if allUserCounter % 100 == 0:
        print(allUserCounter, " SAVED")
        wb.save('res.xlsx')
sheet.cell('H2').value = allUserCounter
sheet.cell('I2').value = junkUserCounter
sheet.cell('J2').value = junkUserCounter / allUserCounter
wb.save('res.xlsx')

f列表存用于去重,数据每隔100个子节点储存一次防止被管理员或者系统弄得颗粒无收。

初步效果

0度用户输入15人,2800个1度节点
843个僵尸粉丝/2800个粉丝 ≈ 30%僵尸粉比例

本文章为个人学习之用,不为断论。

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

推荐阅读更多精彩内容