一个Python小白5个小时爬虫经历

前言

  最近业余在作一个基于.NET Core的搜索项目,奈何基层代码写好了,没有看起来很华丽的数据供测试。很巧的也是博客搜索,因而乎想到了博客园。C#也能作作页面数据抓取的,不过在博客园看到的大部分都是python实现,因此就临时想了一下看看python究竟是什么东东,不看基础语法,不看语言功能,直接上代码,哪里不会搜哪里。代码完成总共用时大概4个小时,其中搭建环境加安装BeautifulSoup大概1个小时。解析HTML用时间最多了,边看demo边解析,大概2个小时,剩下的时间就是调试加保存数据了。html

环境搭建

  既然用python,那么天然少不了语言环境。因而乎到官网下载了3.5版本的。安装完以后,随机选择了一个编辑器叫PyCharm,话说python编辑器还真挺多的。因为本人是小白,因此安装事项不在过多赘述。python

  

  建好项目,打开编辑器,直接开工。原本以前用C#写的时候,大致思路就是获取网页内容,而后正则匹配。后来发现网上的帖子也不少。不过在搜索过程当中发现,不建议用正则来匹配HTML。有正好个人正则不太好,因此我就搜了一下HTML解析工具,果不其然,人家都作好了,直接拿来用吧。没错就是这个东东:BeautifulSoup 。安装也很简单,不过中间出了个小插曲,就是bs4没有。继续搜,而后须要用pip安装一下就行了。(固然我并不知道ps4和pip是什么鬼)git

  

思路分析

  博客吗,我固然就对准了博客园,因而乎,进入博客园首页,查看请求。json

  

发送请求

  固然我不知道python是怎么进行网络请求的,其中还有什么2.0和3.0的不一样,中间曲曲折折了很多,最终仍是写出了最简单的一段请求代码。网络

  

import urllib.parse
import urllib.request

# params  CategoryId=808 CategoryType=SiteHome ItemListActionName=PostList PageIndex=3 ParentCategoryId=0 TotalPostCount=4000
def getHtml(url,values):
    user_agent='Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36'
    headers = {'User-Agent':user_agent}
    data = urllib.parse.urlencode(values)
    response_result = urllib.request.urlopen(url+'?'+data).read()
    html = response_result.decode('utf-8')
    return html

#获取数据
def requestCnblogs(index):
    print('请求数据')
    url = 'http://www.cnblogs.com/mvc/AggSite/PostList.aspx'
    value= {
         'CategoryId':808,
         'CategoryType' : 'SiteHome',
         'ItemListActionName' :'PostList',
         'PageIndex' : index,
         'ParentCategoryId' : 0,
        'TotalPostCount' : 4000
    }
    result = getHtml(url,value)
    return result

  其实博客园这个请求仍是挺标准的,哈哈正好适合抓取。由于他返回的就是一段html。(若是返回json那不是更好。。。。)mvc

数据解析

  上文已经提到了,用到的是BeautifulSoup,好处就是不用本身写正则,只要根据他的语法来写就行了,在屡次的测试以后终于完成了数据的解析。先上一段HTML。而后在对应下面的代码,也许看起来更轻松一些。app

<div class="post_item">
    <div class="digg">
        <div class="diggit" onclick="DiggPost('hyper-xl',6417741,281238,1)">
            <span class="diggnum" id="digg_count_6417741">1</span>
        </div>
        <div class="clear"></div>
        <div id="digg_tip_6417741" class="digg_tip"></div>
    </div>
    <div class="post_item_body">
        <h3><a class="titlelnk" href="http://www.cnblogs.com/hyper-xl/p/6417741.html" target="_blank">Python 字符串格式化</a></h3>


        <p class="post_item_summary">
            <a href="http://www.cnblogs.com/hyper-xl/" target="_blank">
                <img width="48" height="48" class="pfs"
                     src="//pic.cnblogs.com/face/795666/20160421231717.png" alt="" />
            </a>    转载请注明出处 Python2.6+ 增长了str.format函数,用来代替原有的'%'操做符

            。它使用比'%'更加直观、灵活。下面详细介绍一下它的使用方法。 下面是使用'%'的例子: 格式很像C语言的printf是否是?因为'%'是一个操做符,只能在左右

            两边各放一个参数,所以右边多个值须要用元组或 ...
        </p>

        <div class="post_item_foot">
            <a href="http://www.cnblogs.com/hyper-xl/" class="lightblue">新月的力量_141</a>
            发布于 2017-02-19 23:07
            <span class="article_comment">
                <a href="http://www.cnblogs.com/hyper-xl/p/6417741.html#commentform" title="" class="gray">
                    评论(0)
                </a>
            </span>
            <span class="article_view">
                <a href="http://www.cnblogs.com/hyper-xl/p/6417741.html" class="gray">
                    阅读

                    (138)
                </a>
            </span>
        </div>
    </div>
    <div class="clear"></div>
</div>

  经过上文的HTML代码能够看到几点。首先每一条数据都在 div(class="post_item")下。而后 div("post_item_body")下有用户信息,标题,连接,简介等信息。逐一根据样式解析便可。代码以下:编辑器

from bs4 import BeautifulSoup
import request
import re

#解析最外层
def blogParser(index):

  cnblogs = request.requestCnblogs(index)
  soup = BeautifulSoup(cnblogs, 'html.parser')
  all_div = soup.find_all('div', attrs={'class': 'post_item_body'}, limit=20)

  blogs = []
  #循环div获取详细信息
  for item in all_div:
      blog = analyzeBlog(item)
      blogs.append(blog)

  return blogs

#解析每一条数据
def analyzeBlog(item):
    result = {}
    a_title = find_all(item,'a','titlelnk')
    if a_title is not None:
        # 博客标题
        result["title"] = a_title[0].string
        # 博客连接
        result["href"] = a_title[0]['href']
    p_summary = find_all(item,'p','post_item_summary')
    if p_summary is not None:
        # 简介
        result["summary"] = p_summary[0].text
    footers = find_all(item,'div','post_item_foot')
    footer = footers[0]
    # 做者
    result["author"] = footer.a.string
    # 做者url
    result["author_url"] = footer.a['href']
    str = footer.text
    time = re.findall(r"发布于 .+? .+? ", str)
    result["create_time"] = time[0].replace('发布于 ','')

    comment_str = find_all(footer,'span','article_comment')[0].a.string
    result["comment_num"] = re.search(r'\d+', comment_str).group()

    view_str = find_all(footer,'span','article_view')[0].a.string
    result["view_num"] = re.search(r'\d+', view_str).group()

    return result

def find_all(item,attr,c):
    return item.find_all(attr,attrs={'class':c},limit=1)

  上边一堆代码下来,着实花费了我很多时间,边写边调试,边百度~~不过还好最终仍是出来了。等数据都整理好以后,而后我把它保存到了txt文件里面,以供其余语言来处理。原本想写个put直接put到ElasticSearch中,奈何没成功。后边在试吧,毕竟个人重点只是导数据,不在抓取这里。函数

import match
import os
import datetime
import json

def writeToTxt(list_name,file_path):
    try:
        #这里直接write item 便可,不要本身给序列化在写入,会致使json格式不正确的问题
        fp = open(file_path,"w+",encoding='utf-8')
        l = len(list_name)
        i = 0
        fp.write('[')
        for item in list_name:
            fp.write(item)
            if i<l-1:
                fp.write(',\n')
            i += 1
        fp.write(']')
        fp.close()
    except IOError:
        print("fail to open file")

#def getStr(item):
#   return json.dumps(item).replace('\'','\"')+',\n'

def saveBlogs():
    for i in range(1,2):
        print('request for '+str(i)+'...')
        blogs = match.blogParser(i,5)
        #保存到文件
        path = createFile()
        writeToTxt(blogs,path+'/blog_'+ str(i) +'.json')
        print(''+ str(i) +'页已经完成')
    return 'success'

def createFile():
    date = datetime.datetime.now().strftime('%Y-%m-%d')
    path = '/'+date
    if os.path.exists(path):
        return path
    else:
        os.mkdir(path)
        return path

result = saveBlogs()
print(result)

 

  上边呢,我取了一百页的数据,也就是大概2000条作测试。工具

成果验收

  废了好大劲终于写完那些代码以后呢,就能够享受胜利的果实了,虽然是初学者,代码写的很渣,这参考一下,那参考一下,不过仍是有些收获的。运行效果以下:

  

  生成的文件:

  

  文件内容:

  

总结

  一个简单的抓取程序就写完了,python还真是TM的好用。之后有空再研究研究吧。代码行数算上空行和注释总共 100 (50+25+25) 行。凑个整数好看点~~如今认识字我感受就能够上手写程序了。这里百度一下,那里google一下,问题就解决了,程序也出来了,大功告成。

  是时候该和python暂时告别了,继续个人.NET事业。话说上次作rss采集的时候,好多“.NET要完蛋了”,“为何咱们不招.NET” 是什么鬼。 小伙伴们,下次见。

相关文章
相关标签/搜索