《Python网络数据采集》读书笔记(四)

1、遍历单个域名

维基百科那些指向词条页面(不是指向其他内容页面)的链接有三个共同点:

? 它们都在id是bodyContent的div标签里

? URL链接不包含分号

? URL链接都以/wiki/开头

# -*- coding: utf-8 -*-
import re
from urllib.request import urlopen
from bs4 import BeautifulSoup

html = urlopen("http://en.wikipedia.org/wiki/Kevin_Bacon")
bsObj = BeautifulSoup(html, "lxml")
for link in bsObj.find("div", {"id":"bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$")):
    if 'href' in link.attrs:
        print(link.attrs['href'])

运行以上代码,就会看到维基百科上凯文·贝肯词条里所有指向其他词条的链接。

简单地构建一个从一个页面到另一个页面的爬虫:

# -*- coding: utf-8 -*-
import re
import datetime
import random
from urllib.request import urlopen
from bs4 import BeautifulSoup

# 用系统当前时间生成一个随机数生成器
random.seed(datetime.datetime.now())
def getLinks(articleUrl):
    html = urlopen("http://en.wikipedia.org"+articleUrl)
    bsObj = BeautifulSoup(html ,"lxml")
    return bsObj.find("div", {"id":"bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$"))
links = getLinks("/wiki/Kevin_Bacon")
while len(links) > 0:
    newArticle = links[random.randint(0, len(links)-1)].attrs["href"]
    print(newArticle)
    links = getLinks(newArticle)

程序首先把起始页面里的词条链接列表设置成链接列表。然后用一个循环,从页面中随机找一个词条链接标签并抽取href属性,打印这个页面链接,再把这个链接传入getLinks函数重新获取新的链接列表。

2、采集整个网站

首先要做的就是对链接去重,以避免一个页面被重复采集。

接着我们可以打印出“页面标题、正文的第一个段落,以及编辑页面的链接(如果有的话)”。

# -*- coding: utf-8 -*-
import re
from urllib.request import urlopen
from bs4 import BeautifulSoup

pages = set()
def getLinks(pageUrl):
    global pages
    html = urlopen("http://en.wikipedia.org"+pageUrl)
    bsObj = BeautifulSoup(html, "lxml")
    try:
        print(bsObj.h1.get_text())
        print(bsObj.find(id="mw-content-text").findAll("p")[0])
        print(bsObj.find(id="ca-edit").find("span").find("a").attrs['href'])
    except AttributeError:
        print("页面缺少一些属性!不过不用担心!")
        
    for link in bsObj.findAll("a", href=re.compile("^(/wiki/)")):
        if 'href' in link.attrs:
            if link.attrs['href'] not in pages:
            # 我们遇到了新页面
                newPage = link.attrs['href']
                print("----------------\n"+newPage)
                pages.add(newPage)
                getLinks(newPage)
getLinks("")

以上程序一开始用getLinks处理一个空URL(其实是维基百科的主页)。接着打印出需要输出的信息,然后遍历页面上的每个链接,并检查是否已经在全局变量集合pages里面了(已经采集的页面集合)。如果不在,就打印到屏幕上,并把链接加入pages 集合,再用getLinks递归地处理这个链接。

3、通过互联网采集

# -*- coding: utf-8 -*-
import re
import datetime
import random
from urllib.request import urlopen
from bs4 import BeautifulSoup

pages = set()
random.seed(datetime.datetime.now())

# 获取页面所有内链的列表
def getInternalLinks(bsObj, includeUrl):
    internalLinks = []
    # 找出所有以"/"开头的链接
    for link in bsObj.findAll("a", href=re.compile("^(/|.*"+includeUrl+")")):
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in internalLinks:
                internalLinks.append(link.attrs['href'])
    return internalLinks
    
# 获取页面所有外链的列表
def getExternalLinks(bsObj, excludeUrl):
    externalLinks = []
    # 找出所有以"http"或"www"开头且不包含当前URL的链接
    for link in bsObj.findAll("a", href=re.compile("^(http|www)((?!"+excludeUrl+").)*$")):
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in externalLinks:
                externalLinks.append(link.attrs['href'])
    return externalLinks
    
def splitAddress(address):
    addressParts = address.replace("http://", "").split("/")
    return addressParts
    
def getRandomExternalLink(startingPage):
    html = urlopen(startingPage)
    bsObj = BeautifulSoup(html, "lxml")
    externalLinks = getExternalLinks(bsObj, splitAddress(startingPage)[0])
    if len(externalLinks) == 0:
        internalLinks = getInternalLinks(startingPage)
        return getNextExternalLink(internalLinks[random.randint(0, len(internalLinks)-1)])
    else:
        return externalLinks[random.randint(0, len(externalLinks)-1)]
        
def followExternalOnly(startingSite):
    externalLink = getRandomExternalLink("http://oreilly.com")
    print("随机外链是:"+externalLink)
    followExternalOnly(externalLink)
    
followExternalOnly("http://oreilly.com")

上面这个程序从http://oreilly.com开始,然后随机地从一个外链跳到另一个外链。

网站首页上并不能保证一直能发现外链。这时为了能够发现外链,就需要递归地深入一个网站直到找到一个外链才停止。如果爬虫遇到一个网站里面一个外链都没有,这时程序就会一直在这个网站运行跳不出去,直到递归到达Python的限制为止。

如果我们的目标是采集一个网站所有的外链,并且记录每一个外链,可以增加下面的函数:

allExtLinks = set()
allIntLinks = set()
def getAllExternalLinks(siteUrl):
    html = urlopen(siteUrl)
    bsObj = BeautifulSoup(html, 'lxml')
    internalLinks = getInternalLinks(bsObj,splitAddress(siteUrl)[0])
    externalLinks = getExternalLinks(bsObj,splitAddress(siteUrl)[0])
    for link in externalLinks:
        if link not in allExtLinks:
            allExtLinks.add(link)
            print(link)
    for link in internalLinks:
        if link not in allIntLinks:
            print("即将获取链接的URL是:"+link)
            allIntLinks.add(link)
            getAllExternalLinks(link)
getAllExternalLinks("http://oreilly.com")

4、用Scrapy采集

创建Scrapy项目:在当前目录中会新建一个名称也是wikiSpider的项目文件夹。

scrapy startproject wikiSpider

在items.py文件中,定义一个Article类。

# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
#  

from scrapy import Item, Field

class Article(Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = Field()

在wikiSpider/wikiSpider/spiders/文件夹里增加一个articleSpider.py文件。

from scrapy.selector import Selector
from scrapy import Spider
from wikiSpider.items import Article

class ArticleSpider(Spider):
    
    name = "article"
    allowed_domains = ["en.wikipedia.org"]
    start_urls = ["http://en.wikipedia.org/wiki/Main_Page", "http://en.wikipedia.org/wiki/Python_%28programming_language%29"]
    
    def parse(self, response):
        item = Article()
        title = response.xpath('//h1/text()')[0].extract()
        print("Title is: " + title)
        item['title'] = title
        return item

在wikiSpider主目录中用如下命令运行ArticleSpider:

scrapy startproject wikiSpider

陆续出现的调试信息中应该会这两行结果:

Title is: Main Page
Title is: Python (programming language)

*可以在Scrapy项目中的setting.py文件中设置日志显示层级:

LOG_LEVEL = 'ERROR'

Scrapy日志有五种层级,按照范围递增顺序排列如下:CRITICAL,ERROR,WARNING,DEBUG,INFO

也可以输出(追加)到一个独立的文件中:

scrapy crawl article -s LOG_FILE=wiki.log
Title is: Main Page
Title is: Python (programming language)

Scrapy支持用不同的输出格式来保存这些信息,对应命令如下所示:

scrapy crawl article -o articles.csv -t csv
scrapy crawl article -o articles.json -t json
scrapy crawl article -o articles.xml -t xml

也可以自定义Item对象,把结果写入你需要的一个文件或数据库中,只要在爬虫的parse部分增加相应的代码即可。

原文地址:http://blog.51cto.com/9473774/2096292

时间: 2024-11-10 13:41:54

《Python网络数据采集》读书笔记(四)的相关文章

Javascript高级程序设计读书笔记(第六章)

第6章  面向对象的程序设计 6.2 创建对象 创建某个类的实例,必须使用new操作符调用构造函数会经历以下四个步骤: 创建一个新对象: 将构造函数的作用域赋给新对象: 执行构造函数中的代码: 返回新对象. 构造函数的问题:每个方法都要在每个实例上重新创建一遍: 理解原型对象: 无论何时,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象.默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向proto

JavaScript 高级程序设计读书笔记(1)

第6章 面向对象的程序设计 属性的类型 JS 中对象的属性有两种,数据属性和访问器属性(accessor property ),属性有其自身的特性(arrribute),可以理解为关于属性的属性. 数据属性包含4个特性,分别是[[Configurable]], [[Enumerable]], [[Writable]], [[Value]]. 使用 Object.defineProperty 修改属性描述符时,若该属性之前不存在,除了代码里指定的特性值,其他特性值默认将是 false, 举例如下:

JavaScript高级程序设计-读书笔记(6)

第20章 JSON JSON是一个轻量级的数据格式,可以简化表示复杂数据结构的工作量 JSON的语法可以表示一下三种类型的值 l        简单值:使用与JavaScript相同的语法,可以在JSON中表示字符串.数值.布尔值和null.但JSON不支持JavaScript中的特殊数值undefined. “Hello world!” l        对象(属性的值可以是简单值,也可以是复杂类型值,如下这样在对象嵌入对象) { “name” : ”Nicholas”, “age” : 29

JavaScript高级程序设计-读书笔记(2)

第6章 面向对象的程序设计 创建对象 1.最简单方式创建Object的实例,如 var person = new Object(); person.name = “Greg”; person.age = 27; person.job = ”Doctor”; person.sayName = function() { alert(this.name); }; person. sayName(): 缺点:会产生大量重复代码 2.工厂模式:用函数来封装以特定接口创建对象的细节,如 function c

JavaScript高级程序设计读书笔记之OOP

关于JavaScript创建对象的方式: 1.工厂模式 1 function createPerson(name, age, job){ 2 var o = new Object(); 3 o.name = name; 4 o.age = age; 5 o.job = job; 6 o.sayName = function(){ 7 alert(this.name); 8 }; 9 return o; 10 } 11 var person1 = createPerson("Nicholas&qu

JavaScript高级程序设计-读书笔记(4)

第11章 DOM扩展 1.选择符API Selector API Level 1 的核心是两个方法:querySelector()和querySelectorAll().在兼容的浏览器中,可以通过Document及Element类型的实例调用它们.目前完全支持Selector API Level 1的浏览器有IE 8+. Firefox 3.5+. Safari 3.1+. Chrome和Opera 10+. querySelector()方法接收一个CSS选择符,返回与该模式匹配的第一个元素,

JavaScript高级程序设计-读书笔记(3)

第8章 BOM 1.window对象 (1)全局作用域 BOM的核心对象是window,它表示浏览器的一个实例.在浏览器中,window对象既是通过JavaScript访问浏览器窗口的一个接口,又是ECMAScript规定的Global对象. 所有在全局作用域中声明的变量.函数都会变成window对象的属性和方法. (2)窗口关系及框架 如果界面中包含框架,这每个框架都拥有自己的window对象,并且保存在frames集合中,可以通过数值的索引(从0开始,从左至右,从上到下)或者框架名称来访问相

javascript高级程序设计 读书笔记1

第二章  在HTML中使用JS 加载JS有三种:行内,head头部和外部链接JS   最好使用外部链接<script src="example.js" ></script>(推荐),在script标签中不需要放任何代码,放了也会被忽略.也可以使用<script src="example.js" /> 但是语法不符合HTML规范  所以不要这样写. 在传统做法中,script放在head中,但是会等到加载完JS再执行DOM,页面会延

javascript高级程序设计读书笔记2

<!DOCTYPE HTML>//这个网页的文档类型,这个是html5的写法Bootstrap使用的某些HTML元素和CSS属性需要文档类型为HTML5 doctype.因此这一文档类型必须出现在项目的每个页面的开始部分 <html lang="en">//这里的lang="en"可以删除,如果不删除的,用谷歌之类打开,它会认为是英文的,会自动给翻译(如果设置了自动翻译的话) 有两个版本的 jQuery 可供下载:Production ver

Javascript高级程序设计读书笔记(第二章)

第二章  在HTML中使用Javascript 2.1<script>元素 延迟脚本(defer = "defer")表明脚本在执行时不会影响页面的构造,脚本会被延迟到整个页面都解析完毕后再运行.相当于告诉浏览器立即下载,但延迟执行.HTML5规定要求脚本按照它们出现的先后顺序执行. 异步脚本(async)如果有多个脚本文件,执行顺序不确定,指定async属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容. XHTML代码的规则比编写HTML严格得多,下面