你可能需要灵活地控制要抓取哪些网站以及如何抓取,还需要一种在不需要编写很多代码的情况下,尽可能快地添加新网站或者修改已有网站的方法。
1.规划和定义对象
当决定抓取哪些数据时,最好的做法是忽视所有的网站。要自问“我需要什么?”,然后想
方设法从中寻找所需信息。
对你考虑的每一项都做一个清单检查,问自己:
- 这个信息可以帮助项目实现目标吗?是否可有可无?
- 信息可能之后需要抓取,后抓取的难度大吗?
- 数据是否冗余?
- 将数据存储在这个对象中是否符合逻辑?(例如产品在不同网站描述不一样,则无需存储描述信息)
确定抓取数据,问自己以下问题,然后确定如何在代码中存储并处理这些数据。
- 该数据是稀疏的还是密集的?
- 数据有多大?
- 在数据较大的情况下,每次运行分析时都需要检索该数据还是偶尔检索?
- 这种类型的数据有多大的变化性?需要经常加入新的属性、修改类型还是保持不变?
发现数据是稀疏的(仅有少数事物有这些属性之一),因此你可能决定经常增加或者移除部分属性。
认真思考并规划你究竟需要抓取什么以及如何存储变得非常关键。
2.处理不同的网络布局
最显而易见的方法是,为每个网站单独编写一个网络爬虫或者页面解析器。每个爬虫或解析器以一个 URL、字符串或者 BeautifulSoup 对象作为输入,并返回一个抓取的 Python 对象。
以下是一个 Content 类的示例(代表网站上的一块内容,如新闻文章),其中两个抓取器函
数以 BeautifulSoup 对象作为输入,返回一个 Content 实例:
import requests
class Content:
def __init__(self, url, title, body):
self.url = url
self.title = title
self.body = body
def getPage(url):
req = requests.get(url)
return BeautifulSoup(req.text, 'html.parser')
def scrapeNYTimes(url):
bs = getPage(url)
title = bs.find('h1').text
lines = bs.find_all('p', {
'class': 'story-content'})
body = '\n'.join([line.text for line in lines])
return Content(url, title, body)
def scrapeBrookings(url):
bs = getPage(url)
title = bs.find('h1').text
body = bs.find('div', {
'class': 'post-body'}).text
return Content(url, title, body)
url = 'https://www.brookings.edu/blog/future-development/2018/01/26/delivering-inclusive-urban-access-3-uncomfortable-truths'
content = scrapeBrookings(url)
print('Title: {}'.format(content.title))
print('URL: {}\n'.format(content.url))
print(content.body)
url = 'https://www.nytimes.com/2018/01/25/opinion/sunday/silicon-valley-immortality.html'
content = scrapeNYTimes(url)
print('Title: {}'.format(content.title))
print('URL: {}\n'.format(content.url))
print(content.body)
两个抓取器函数都是做同样的事情,唯一不同的是用于获取信息的 CSS 选择器变量。使用BeautifulSoup的find和find_all函数传入标签和键值对属性的字典两个参数来定位目标数据的位置,为了更简便,用单个 CSS 选择器,使用BeautifulSoup 的 select 函数选定你希望抓取的信息,并且将这些选择器放入到一个字典对象中。
str.join(sequence) join()返回通过指定字符连接序列中元素后生成的新字符串
更新Content类并新建Website类,然后新建Crawler 类就能去抓取任何网站的任何网页的标题和内容,代码如下:
# -*- coding: GBK -*-
import requests
from bs4 import BeautifulSoup
class Content:
'''所有文章/网页的共同基类'''
def __init__(self, url, title, body):
self.url = url
self.title = title
self.body = body
def print(self):
'''用灵活的打印函数控制结果'''
print('URL: {}'.format(self.url))
print('TITLE: {}'.format(self.title))
print('BODY:\n{}'.format(self.body))
class Website:
'''描述网站结构的信息'''
def __init__(self, name, url, titleTag, bodyTag