Python 爬虫入门课作业3-爬虫基础

课程作业

  • 选择第二次课程作业中选中的网址
  • 爬取该页面中的所有可以爬取的元素,至少要求爬取文章主体内容
  • 可以尝试用lxml爬取

作业网址

http://www.jianshu.com/p/e0bd6bfad10b

网页爬取

分别用Beautiful Soup和lxml做了爬取:

  • 主页面所有链接,写到 _all_links.txt文件
  • 分别抓取各链接,获取文章主体内容和title, 并保存主体内容到以title命名
    的文件
  • 对于无title或无主体内容的链接,将url写到Title_Is_None.txt文件中

最后的输出图:


输出

框架结构:

  • spidercomm.py: 定义一些公用函数,如写文件,下载页面等,这些函数独立于实际使用的爬取方式

  • spiderbs4.py: 定义用BeautifulSoup实现需要的一些函数,如爬取链接,爬取页面内容

  • spiderlxml.py: 定义用lxml实现需要的一些函数,如爬取链接,爬取页面内容

  • bs4.py: 用BeautifulSoup实现的爬取客户端

  • lxml.py: 用lxml实现的爬取客户端

文件夹结果图:

图片.png

BeautifulSoup 实现

BeautifulSoup实现代码

1 导入 spidercomm 和 BeautifulSoup 模块

# -*- coding: utf-8 -*-
import spidercomm as common
from bs4 import BeautifulSoup

2 使用BeautifulSoup 找到所有tag a,spidercomm 模块的爬取所有链接的方法会用到其返回值。

# get all tags a from a single url
def a_links(url_seed,attrs={}):
    html = common.download(url_seed)
    soup = BeautifulSoup(html,'html.parser')
    alinks= soup.find_all('a',attrs)
    return alinks

3 使用BeautifulSoup 爬取某个url的页面,主要关注title和文章主体。

def crawled_page(crawled_url):
        html = common.download(crawled_url)
        soup = BeautifulSoup(html,'html.parser')
        title = soup.find('h1',{'class':'title'})
        if title== None:
            return "Title_Is_None",crawled_url
        content = soup.find('div',{'class':'show-content'})
        if content == None:
            return title.text, "Content_Is_None"
        return title.text,content.text

4 判断是否分页

def isMultiPaged(url):    
    html_page1 = common.download(url % 1)
    soup = BeautifulSoup(html_page1,'html.parser')
    body1 = soup.find('body')   
    body1.script.decompose()
       
    html_page2 = common.download(url % 2)
    if html_page2 == None:
        return False
    soup = BeautifulSoup(html_page2,"html.parser")
    body2 = soup.find('body')
    #print [x.extract() for x in body2.findAll('script') ]
    body2.script.decompose()
    if str(body1) == str(body2):
        return False
    else:
        return True

5 获取所有分页数

def getNumberOfPages(url):
    count = 1
    flag = True
    if (isMultiPaged(url)):
        while flag:
            url= url % count
            # print "url: %s" % url
            count += 1
            html = common.download(url)
            if html==None:
                break        
    return count

BeautifulSoup客户端代码

1 导入spidercomm 和 spiderbs4 模块

# -*- coding: utf-8 -*-
import os
import spiderbs4 as bs4
import spidercomm as common

2 设置所需的变量,创建输出结果路径

# set up             
url_root = 'http://www.jianshu.com/'
url_seed = 'http://www.jianshu.com/p/e0bd6bfad10b?page=%d'
spider_path='spider_res/bs4/'
if os.path.exists(spider_path) == False:
    os.makedirs(spider_path)

3 调用spidercomm的getNumberOfPages方法,判断要爬取的页面是否分页

# get total number of pages
print "url %s has multiple pages? %r" % (url_seed,bs4.isMultiPaged(url_seed))
page_count = bs4.getNumberOfPages(url_seed)
print "page_count is %s" % page_count

4 调用spidercomm的to_be_crawled_links方法,对所要爬取的页面获取所有链接,如果有分页,则分别获取各页并归并获取的链接,并最终将所有链接写入_all_links.txt文件

# get all links to be crawled and write to file    
links_to_be_crawled=set()
for count in range(page_count):
    links = common.to_be_crawled_links(bs4.a_links(url_seed % count),count,url_root,url_seed)
    print "Total number of all links is %d" % len(links)
    links_to_be_crawled = links_to_be_crawled | links
with open(spider_path+"_all_links.txt",'w+') as file:
    file.write("\n".join(unicode(link).encode('utf-8',errors='ignore') for link in links_to_be_crawled))

5 循环所获取的链接列表,爬取每个链接的页面title和页面文章内容,分别写入以title命名的文件中,如无title,则写入Title_Is_None.txt文件中。

# capture desired contents from crawled_urls
if len(links_to_be_crawled) >= 1:
    for link in links_to_be_crawled:           
      title,content=bs4.crawled_page(link) 
      # print "title is %s" % title                
      file_name = spider_path + title +'.txt'
      common.writePage(file_name,content)

lxml 实现

lxml 实现代码

1 导入lxml模块

# -*- coding: utf-8 -*-
import spidercomm as common
import urlparse
from lxml import etree

2 使用lxml找到所有tag a,spidercomm 模块的爬取所有链接的方法会用到其返回值。

# get all tags a from a single url
def a_links(url_seed,attrs={}):
    html = common.download(url_seed)
    tree = etree.HTML(html)
    alinks= tree.xpath("//a")
    return alinks

3 调用spidercomm的getNumberOfPages方法,判断要爬取的页面是否分页

def crawled_page(crawled_url):
        html = common.download(crawled_url)
        tree = etree.HTML(html)
        title= tree.xpath("/html/body/div[1]/div[1]/div[1]/h1")
        if title == None or len(title) == 0:
            return "Title_Is_None",crawled_url
        contents = tree.xpath("/html/body/div[1]/div[1]/div[1]/div[2]/*")
        if contents == None or len(contents) ==0:
            return title.text, "Content_Is_None"
        content = ''
        for x in contents:
            if (x.text != None):
                content = content + x.xpath('string()')
        return title[0].text,content

4 判断是否分页

def isMultiPaged(url):    
    html_page1 = common.download(url % 1)
    tree = etree.HTML(html_page1)    
    xp1 = tree.xpath("/html/body/div[1]/div[1]/div[1]/div[2]/*")
    xp1 = ",".join(x.text for x in xp1)
    html_page2 = common.download(url % 2)
    if html_page2 == None:
        return False
    tree = etree.HTML(html_page2)    
    xp2 = tree.xpath("/html/body/div[1]/div[1]/div[1]/div[2]/*")
    xp2 = ",".join(x.text for x in xp2)
    if xp1 == xp2:
        return False
    else:
        return True

5 获取所有分页数

def getNumberOfPages(url):
    count = 1
    flag = True
    if (isMultiPaged(url)):
        while flag:
            url= url % count
            print "url: %s" % url
            count += 1
            html = common.download(url)
            if html==None:
                break        
    return count   

lxml 客户端代码

同前面的bs4客户端,只是导入的是spiderlxml模块及调用。

公用方法代码

1 导入所需模块

# -*- coding: utf-8 -*-
import urllib2
import time
import urlparse

2 下载页面

def download(url,retry=2):
   # print "downloading %s" % url
    header = {
            'User-Agent':'Mozilla/5.0'
            }
    try:
        req = urllib2.Request(url,headers=header)
        html = urllib2.urlopen(req).read()
    except urllib2.HTTPError as e:
            print "download error: %s" % e.reason
            html = None
            if retry >0:
                print e.code
                if hasattr(e,'code') and 500 <= e.code < 600:
                    print e.code
                    return download(url,retry-1)
                    time.sleep(1)
    return html

3 将爬取内容写入文件

def writePage(filename,content):
    content = unicode(content).encode('utf-8',errors='ignore')+"\n"
    if ('Title_Is_None.txt' in filename):
        with open(filename,'a') as file:
            file.write(content)
    else:
        with open(filename,'wb+') as file:
            file.write(content)

4 获取单一url的所有外链接

# get urls to be crawled
#:param alinks: list of tag 'a' href, dependent on implementation eg. bs4,lxml
def to_be_crawled_link(alinks,url_seed,url_root):
    links_to_be_crawled=set()
    if len(alinks)==0:
        return links_to_be_crawled
    print "len of alinks is %d" % len(alinks)
    for link in alinks:
        link = link.get('href')            
        if link != None and 'javascript:' not in link:
            if link not in links_to_be_crawled:
                realUrl = urlparse.urljoin(url_root,link)
                links_to_be_crawled.add(realUrl)
    return links_to_be_crawled

5 获取指定分页的所有外连接

def to_be_crawled_links(alinks,count,url_root,url_seed):
    url = url_seed % count
    links = to_be_crawled_link(alinks,url_root,url)#,{'class':'title'})
    links.add(url)
    return links

结语:

实现的还很粗糙,抓取的内容也很简单,希望和大家一起讨论,并进一步完善框架。

两种实现爬取下来的页面内容似乎差不多,格式有些差别,bs4的要好一些,可能是自己代码没处理好,需要再研究下,以后要完善的地方还好多,作业先提交吧。

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

推荐阅读更多精彩内容

  • 本文分享的大体框架包含以下三部分 (1)首先介绍html网页,用来解析html网页的工具xpath(2)介绍pyt...
    不忘初心c阅读 2,548评论 0 14
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,832评论 25 707
  • 声明:本文讲解的实战内容,均仅用于学习交流,请勿用于任何商业用途! 一、前言 强烈建议:请在电脑的陪同下,阅读本文...
    Bruce_Szh阅读 12,685评论 6 28
  • 发个功夫
    juncheung阅读 143评论 0 0
  • 从事婴幼儿早期教育快两年之久,可以说也接触过上千个0-3岁婴幼儿,对0-3岁婴幼儿早期教育的重要性有一些想法分...
    Merphy1阅读 1,813评论 5 1