【实战】还记得校内网么(人人网)?当年的同学都在哪?爬取一下就知道

引言

不知从何时起,10.24变成了程序员的节日,首先祝大家节日快乐!代码永无bug!

 

小编最近突然有点怀旧,想到了一个古老的网站——校内网(人人网),在小编还在读大学的那个时间,校内网真的是火的一塌糊涂,那时候的同学每天都在不停的刷校内,找同学,发布新鲜事。然而世事难料,谁也不曾想到当时那么火的校内网现在变得这么凄凉,如果不是刻意去想,可能都想不到这个网站。当想到这个网站的时候,小编突然想去访问一下这个网站,打开浏览器,输入用户名密码,还好,账号能够登陆。看着自己的好友列表,很想知道他们当时都在哪个城市。所以就写了几行代码,查询一下,也就有了今天的项目。

 

需求分析

爬取校内网个人主页的好友列表,然后再一次访问好友的好友列表页面,获取用户的城市信息。

 

需要源码的同学可以关注公众号,回复“校内网”获取源码。

 

知识点

 

爬取数据:WebDriver

多线程:ThreadPoolExecutor

图形分析:pyecharts

数据库:Mongo

 

获取好友列表

 

由于使用WebDriver操作浏览器,模拟用户登录操作,所以网页不需要进行过多的分析,只要能够定位控件位置即可。此处说一下逻辑,首先登陆校内网→访问个人主页→访问个人好友页面→提取好友信息(包括:姓名,好友主页地址,省份)→将提取的信息存入mongoDB。

 

从mongoDB中读取需要爬取的好友列表(用status标识)→访问好友个人主页(此处由于校内网限制,不能直接访问,需要登录自己账号后才可访问)→访问好友的好友列表→提取好友信息→将提取的信息存入mongoDB。

 

此处本应可以无限循环,例如我们提取到了好友的好友列表后,还可以提取好友的好友的好友列表。但是由于校内网隐私设置,我们无法访问好友的好友的好友列表页面,此处只能作罢。

 

之前听说过一句话,大概意思是“通过六个人,你可以认识世界上任何一个人”,本来还想着通过校内查询六次看看能够查询到多少人,结果受到校内设置影响,只能查询一次。

 

以下是部分代码:

 

登陆校内网:

 

def login(driver,url,username,pwd):

    driver.get(url)

    # 输入用户名
    input_text = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_id("email")
    )
    input_text.send_keys(username)
    print("输入用户名 ok")

    # 输入密码
    password = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_id("password")
    )
    password.send_keys(pwd)
    print("输入密码 ok")

    # 检查是否有需要输入验证码
    check_code(driver)

    # 点击登录按钮
    login_button = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_id("login")
    )
    login_button.click()
    print("点击登录 ok")

    # 进入个人主页
    hd_name = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_class_name("hd-name")
    )
    href = hd_name.get_attribute("href")
    print("找到个人主页地址 ok")
    return  href

 

爬取个人主页好友列表:

 

def get_my_friends_info_list(driver,href):

    # 访问个人主页地址
    driver.get(href)
    name = driver.title
    print("访问%s的个人主页 ok"%name)

    # 进入好友列表页面
    friends_button = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_xpath('//*[@id="specialfriend-box"]/div[1]/div/h5/a')
    )
    friends_button.click()
    print("进入%s好友列表 ok"%name)

    # 等待5s,加载数据
    time.sleep(5)

    # 如果滚动条不是在页面最下方,证明列表需要加载,下拉至页面最下方
    len_after = 0
    len_before = 1

    # 如果下拉后的好友数量不等于下拉前好友的数量,就继续下拉页面
    while len_after != len_before:
        friends_list = WebDriverWait(driver, timeout).until(
            lambda d: d.find_elements_by_class_name("friend-detail")
        )

        # 获取下拉之前的好友数量
        len_before = len(friends_list)

        # 下拉至页面最底端
        js = "window.scrollTo(0,document.body.scrollHeight)"
        driver.execute_script(js)
        time.sleep(2)

        friends_list = WebDriverWait(driver, timeout).until(
            lambda d: d.find_elements_by_class_name("friend-detail")
        )

        # 获取下拉后的好友数量
        len_after = len(friends_list)
        print("滚动条向下滚动到底部一次")

    # 定位 friend-detail 标签
    friends_list = WebDriverWait(driver, timeout).until(
        lambda d: d.find_elements_by_class_name("friend-detail")
    )
    print("定位li标签 ok")

    # 定位 .friend-detail a 标签
    personal_home_pages = WebDriverWait(driver, timeout).until(
        lambda d:d.find_elements_by_css_selector(".friend-detail a")
    )
    print("定位a标签 ok")

    # 定位省份信息
    provinces_list = WebDriverWait(driver, timeout).until(
        lambda d: d.find_elements_by_class_name("friends-loc-info")
    )
    print("定位省份信息 ok")

    # 将定位的信息组装成字典并写入数据库
    for i in range(len(friends_list)):
        friends_info = {"data_id":friends_list[i].get_attribute("data-id"),
                        "name":friends_list[i].get_attribute("data-name"),
                        "id":friends_list[i].get_attribute("id"),
                        "personal_home_page":personal_home_pages[i].get_attribute("href"),
                        "province":provinces_list[i].get_attribute("title"),
                        "status":0,
                        "from":name,
                        }
        print(friends_info["province"], friends_info["name"])

        #将获取到的数据写入数据库
        write_to_mongo(friends_info,i,name)

    driver.quit()

 

存储数据到mongoDB:

 

def write_to_mongo(friends_info,i,name):

    # 将数据写入数据库
    curr = pymongo.MongoClient()
    database = curr["renren"]
    coll = database['friends']
    coll.insert(friends_info)
    print("%s的第%s个好友写入数据库成功"%(name,i+1))
    curr.close()

数据分析:

 

def fenxi():
    # 读取数据库中的数据
    curr = pymongo.MongoClient()
    database = curr["renren"]
    coll = database['friends']
    friends_list_all_find = coll.find({})
    friends_list_all = []
    for f in friends_list_all_find:
        friends_list_all.append(f)
    curr.close()

    # 统计省份信息
    result = {}
    for f in friends_list_all:
        if f["province"] not in result.keys():
            result[f["province"]] = 1
        else:
            result[f["province"]] += 1

    # 对统计数据进行过滤,去除没有省份的数据和小于100的数据
    result_gt100 = {}
    for k,v in result.items():
        if v >= 100:
            if k.strip():
                result_gt100[k] = v

    # 对数据按照省份值进行排序
    result_gt100_sorted = sorted(result_gt100.items(), key=lambda x: x[1], reverse=True)

    # 统计出key列表和value列表,下面生成统计图使用
    key = []
    value = []
    for k in result_gt100_sorted:
        key.append(k[0])
        value.append(k[1])
    # 柱形图
    bar = Bar("柱状图",width=2000,height=1000)
    bar.add("",key,value,is_label_show=True)
    bar.render(path="柱状图.html")

    # 全国地图
    map = Map("全国地图示例", width=2000, height=1000)
    map.add("", key, value, maptype='china',is_visualmap=True, visual_text_color="#000",is_map_symbol_show = False,
            visual_range=[0, 1000])
    map.render(path="地图.html")

    # 词云图
    wordcloud = WordCloud(width=2000, height=1000)
    wordcloud.add("", key, value, word_size_range=[20, 100])
    wordcloud.render(path="词云.html")

 

数据分析结果

 

本人校内网好友154位,通过这154位好友,一共抓取到了27215位好友信息(本人笔名27315,跟这个数字就差100),这里面未对好友进行去重,如果想要更精准一些,需要按照id去重。其中一位同学居然有3345个好友。。。嗯,是在下输了。

 

爬取到这些结果后,对数据进行了过滤,去除掉小于100的数据,再去除掉空数据(空数据是因为有些人未设置地区属性)。下面是结果分析。

 

地区分布柱形图:

可以看到有一个省份独占鳌头,相信同学们应该能够通过此图猜到小编是哪个省份的了。没错,就是排在第一位的黑龙江。

 

去除掉黑龙江之后,我们再来看一下结果:

省份还是比较平均的。

 

地图分布:

通过此图可以看见,好友分布由东北向西南递减。注:空白地区不是没有数据,而是数据过少被过滤掉了。

 

最后看下词云结果:

 

源码

链接:https://pan.baidu.com/s/1yXS3ryDV2PfFFKN9xdjZAg 密码:k4nz

 

以上内容为第一次爬取,后来在朋友的建议下做了优化:

昨天发布了爬取校内网好友分布的代码,然而还不够完善,在多线程的部分听取了一位朋友的意见,使用了submit方法,保证可以循环多次。另外将提取好友信息的部分单独拿出来做成了一个方法,方便代码以后的管理。由于是在昨日的代码中进行了优化,这次我就不写内容了,直接放上源代码。大家可以按需下载。

 

想要提醒大家的是,多线程如果开的太多,会导致校内网在登陆的时候使用验证码验证,目前还没有有效的自动识别验证码的方法,所以只能开少量线程,本人目前开的是3个,实测5个多线程在爬取第二轮的时候就会出现验证码,大家还是要控制爬取速度。

源码:

链接:https://pan.baidu.com/s/1RWQaR-MJbBH-CdRyIr994g 密码:kt42

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值