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_(programming_language)"]
- 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 部分增加相应的代码即可.
Python 网络数据采集读书笔记(四)
来源: http://www.bubuko.com/infodetail-2556713.html