发现群组(三)根据偏好聚类

        本文采样豆瓣读书的数据,而后根据Tanimoto系数计算相关度,最后分级聚类。

数据采样准备

源码分析

豆瓣读书的网页源码通过浏览器可以查看,下面的代码为截取的相关部分,整个代码在<li></li>标签中,我们要将此标签内容解析,获取最后包含书名的title字段。

  
  <li class="subject-item">
    <div class="pic">
      <a class="nbg" href="https://book.douban.com/subject//" 
        οnclick="moreurl(this,{i:'0',subject_id:'',from:'book_subject_search'})">
        <img class="" src="https://img1.doubanio.com/mpic/s1738643.jpg"
          width="90">
      </a>
    </div>
    <div class="info">
      <h2 class="">      
  
       <a href="https://book.douban.com/subject/xx/" title="枪炮、病菌与钢铁" 
         οnclick="moreurl(this,{i:'0',subject_id:'xx',from:'book_subject_search'})">

    枪炮、病菌与钢铁


    
      <span style="font-size:12px;"> : 人类社会的命运 </span>
       </a>
      </h2>    
    </div>
  </li>

beautiful soup

此工具开源,解析网页。本文中使用它的如下方法:

(1)  soup('标签')。例如 soup('li'),用于获取<li>标签的所有内容。

(2)soup.title.string  。获取title的内容,例如用soup获取csdn的页面内容后,通过此字段可以解析出其title为"

CSDN.NET - 全球最大中文IT社区,为IT专业技术人员提供最全面的信息传播和服务平台
"

(3)find 方法。例如li.find('div', class_='info'),即找上述html源码中的

<div class="info">
部分代码。

(4)get_text() 方法。返回文本,对每一个BeautifulSoup处理后的对象得到的标签都是生效的。例如通过此方法得到最终的书名信息

代码实现

for li in soup('li'):  #针对每个<li>标签进行处理
    if ('class' in dict(li.attrs) and li['class']==['subject-item']):  #如果<li>标签中存在属性class,并且此属性的值为subject-item,则认为是我们要找的项
        m_order = (li.find('div', class_='info'))	#再进一步处理找到的li标签,找到其内部的div标签 
        books.append([m_order.a.get_text()])            #将div标签内的文本内容,放到一个列表中。这样每个<li>下的书名作为此列表的一个元素

for book in books:
    bookstr.append(re.sub(r'\s+','',"".join(book)))    #对于列表每个元素通过'\s'(去除空白字符)正则表达式去掉其中的回车和空格符号,形成最后的书名。


第一个for循环,生成的列表内容如下:

[u'\n\n    \u7b56\u7565\u601d\u7ef4\n\n\n    \n       : \u5546\u754c\u3001\u653f
\u754c\u53ca\u65e5\u5e38\u751f\u6d3b\u4e2d\u7684\u7b56\u7565\u7ade\u4e89 \n']

[u'\n\n    \u5982\u4f55\u9ad8\u6548\u5b66\u4e60\n\n\n    \n       : 1\u5e74\u5b8
c\u6210\u9ebb\u7701\u7406\u5de54\u5e7433\u95e8\u8bfe\u7a0b\u7684\u6574\u4f53\u60
27\u5b66\u4e60\u6cd5 \n']
[u'\n\n    \u5251\u6865\u56fd\u9645\u82f1\u8bed\u8bed\u97f3\u6559\u7a0b\n\n\n
 \n\n  ']

[u'\n\n    \u767e\u5e74\u5b64\u72ec\n\n\n    \n\n  ']
[u'\n\n    \u7231\u60c5\u7b14\u8bb0\n\n\n    \n\n  ']
[u'\n\n    \u4fee\u70bc\u5f53\u4e0b\u7684\u529b\u91cf\n\n\n    \n\n  ']
[u'\n\n    \u6c89\u9ed8\u7684\u5927\u591a\u6570\n\n\n    \n       : \u738b\u5c0f
\u6ce2\u6742\u6587\u968f\u7b14\u5168\u7f16 \n']
[u'\n\n    Macroeconomics\n\n\n    \n\n  ']
[u'\n\n    Principles of Micro-economics\n\n\n    \n\n  ']
[u'\n\n    Intermediate Accounting\n\n\n    \n\n  ']
[u'\n\n    \u72ec\u5531\u56e2\uff08\u7b2c\u4e00\u8f91\uff09\n\n\n    \n\n  ']
[u'\n\n    \u7a7f\u8d8a\u65f6\u7a7a\u7684\u667a\u6167\n\n\n    \n       : \u6d1b
\u4e66\u4eba\u4e0e\u5343\u53e4\u4e4b\u8c1c \n']
[u'\n\n    \u66a7\u6627\u7684\u54c1\u4f4d-\u738b\u5bb6\u536b\u7684\u7535\u5f71\u
4e16\u754c\n\n\n    \n\n  ']
[u'\n\n    \u77e5\u541b\u7528\u5fc3\u5982\u65e5\u6708\n\n\n    \n       : \u53e4
\u5178\u540d\u753b\u7684\u6df1\u5a49\u66f2\u610f \n']
[u'\n\n    \u5251\u6865\u96c5\u601d\u8bed\u6cd5\n\n\n    \n       : \u5251\u6865
\u96c5\u601d\u8bed\u6cd5 \n']

抓取数据

抓取12个豆瓣用户“想读的书”的数据信息,对于有两个及以上人想读的书,则保存到行中。最终形成一个以书名为行,人名为列的表格。

代码实现

#!/usr/bin/python
# coding=utf-8
#’Ctrl+Shift+F2‘即可打开HttpFox
import urllib2
from bs4 import BeautifulSoup
import re
import codecs
import time



#解析每个url链接
def ParseUrl(response,booksOwners):  
    books=[]  
    killSpace=re.compile(r'\s+')    #去除书名中的回车和空格 
    filterName=re.compile(r'\s+')         # r'\s+' 去除title中" 想读的书及数目总数"  u'\u60f3\u8bfb\u7684\u4e66'
    delWishbook=re.compile(u'\u60f3\u8bfb\u7684\u4e66')
    soup=BeautifulSoup(response.read(),"lxml")
    userName=filterName.sub('',soup.title.string)
    userName=delWishbook.sub('',userName)
    print userName   
    
    for li in soup('li'):
        if ('class' in dict(li.attrs) and li['class']==['subject-item']):   #针对页面中的<li>标签进行处理,找到存在书名的标签
            bookInfo = (li.find('div', class_='info'))            
            book=killSpace.sub('',bookInfo.a.get_text())
            books.append([book])        #此处将书名添加到列表books中
            booksOwners.setdefault(book,{})
            booksOwners[book][userName] = 1
    return userName,books   #返回用户及他/它关注书名的列表
#保存书籍
def  SaveBookInfo(userName,books,booksOwners):
    out=codecs.open('doubanwishbook.txt','w','utf-8')
    out.write('Book')
    for user in userName:  #第一行为用户名字,即列表头
        out.write('    ')
        out.write(user)
    out.write('\n')
    #写入书籍
    for book,owners in booksOwners.items():  #针对每本书,查看ower个数
        if len(owners)>1:  #想看此书的人多于一个
            out.write(book)
            for user in userName: #对于每个阅读次数的作者
                if user in owners.keys():
                    out.write('\t1')
                else:
                    out.write('\t0')                
            out.write('\n')
    out.close()
    
#读取url文件,解析各个用户读过的书籍
#输出itemOweners,为字典形式,键为书籍;值为用户和用户是否读过此书籍
def  UserCollectBook():
    user_agent="Mozilla/5.0 (Windows NT 5.1; rv:44.0) Gecko/20100101 Firefox/44.0"
    #两种请求方式在请求的header不同,ajax异步请求比传统的同步请求多一个头参数:X-Requested-With  
    header={'User-Agent':user_agent,'X-Requested-With':'XMLHttpRequest'}
    userName=[]
    books=[]  
    booksOwners={}
    
    file=codecs.open('doubanbookurl.txt','r','utf-8')
    for line in file.readlines():  #每行为一个url,针对每个url进行数据解析
        request=urllib2.Request(line,headers=header)
        print line
        time.sleep(120)   #延时两分钟解析下一个
        try:
            response=urllib2.urlopen(request)
        except urllib2.URLError,e:
            print e.code
            print e.reason
        else:
            user,books=ParseUrl(response,booksOwners)
            userName.append(user)            
    SaveBookInfo(userName,books,booksOwners)
    
          

if __name__ =='__main__':
    UserCollectBook()

数据

Book    黛安Diane(159)    proware(5)    阿鱼(85)    handyware(5)    擦柱而出(92)    相忘于江湖(0)    蔡阿斌(41)    了然(140)    悼玉轩主人(0)    Granadilla(461)    商界丑小鸭(142)    卢墨(23)
反脆弱:从不确定性中获益    1    0    0    0    0    0    1    0    0    1    1    0
经济学的真相(第2版)    1    1    0    0    0    0    0    0    0    0    0    0
深入解析SAS:数据处理、分析优化与商业应用    1    1    0    0    0    0    0    0    0    0    0    0
数学之美    1    1    1    0    1    0    0    1    0    0    0    0

Tanimoto系数

#Tanimoto 系数
def tanimoto(v1,v2):
    c1,c2,shr=0,0,0

    for i in range(len(v1)):
        if v1[i] !=0: c1+=1   #出现在v1中
        if v2[i] !=0: c2+=1    #出现在v2中
        if v1[i]!=0 and v2[i] !=0: shr+=1 #在两个向量中都出现 
    return 1.0-(float(shr)/(c1+c2-shr))   

分级聚类结果

---
  反脆弱:从不确定性中获益
  ---
    数学之美
    ---
      经济学的真相(第2版)
      深入解析SAS:数据处理、分析优化与商业应用


参考资料

1. beautiful soup 使用

python BeautifulSoup使用方法详解

python 中BeautifulSoup入门

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

proware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值