想了想决定,把想要爬的网站都汇总起来,一方面是这样更方便清晰,另一方面也是督促自己每天都编程。

完成后会顺带写上关键点和一些心得。

先定个小目标,爬他100个

网易云音乐的音乐

花瓣采集的图片

instagram的图片

直接爬取

最简单的爬取方式大概就是直接爬取了,单纯使用request方法就可以拿到目标信息,不需要添加头文件也不需要进行模拟登录,甚至动态加载的部分都可以通过instagram的更多的herf进行跳转从而避开抓包步骤。但是考虑到效率问题,只适用于较少链接的爬取,发帖数应当少于200。

示例代码

维基百科编辑ip分布地图

微博情绪地图

拉勾网实习搜索

matplotlib示例代码下载

项目说明

采用scrapy框架可以快速下载,使用FilesPipeline(可以看作是特殊的下载器,通过item的一个特殊字段将目标文件的url传递给它,就可以自动下载,并将下载结果信息存入item的另一个字段,方便查阅)。

FilesPipeline ImagesPipeline
导入路径 scrapy.pipelines.files.FilesPipeline secrapy.pipeline.images.ImagesPipeline
Item字段 file_urls,files image_urls,images
下载目录 FILE_STORE IMAGES_STORE

页面分析

  • 使用scrapy shell来下载目标界面,然后调用view函数在浏览器查看。
  • 发现例子页面链接,使用LinkExtractor提取所有例子页面链接。
  • 分析例子页面,使用fetch函数进入下载第一个例子页面,调用view函数查看。
  • 分析下载地址,完成。

编码实现

  • 创建Scrapy项目。
  • 在配置文件启用FilesPipeline,指定下载目录。
  • 实现Item。
  • 实现Spider。

下载完成后,发现文件名为一串数字,这些数字是下载文件url的sha1散列值。这种命名方式可以防止重名文件相互覆盖,但并不直观。

实现一个FilesPipeline的子类,覆写file_path方法来实现期望的文件命名规则。

1
2
3
4
5
6
7
8
9
from scrapy.pipelines.files import FilesPipeline
from urllib.parse import urlparse
from os.path import basename, dirname, join
class MatpoltlibexamplePipeline(FilesPipeline):
def file_path(self, request, response=None, info=None):
path=urlparse(request.url).path
return join(basename(dirname(path)), basename(path))

杭电图书馆图书

豆瓣阅读热门标签

项目说明

爬取豆瓣阅读所有热门标签下的书籍名称,评分和评价人数。

这个项目的起因是有朋友需要对豆瓣阅读的信息进行数据挖掘,刚好自己没什么事情就随手写了,具体实现并不困难。

值得学习

utf8格式存储json

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
class DoubannovelPipeline(object):
def __init__(self):
self.file = codecs.open('douban_novel_utf8.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item
def spider_closed(self, spider):
self.file.close()

随机选择代理

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class DoubannovelSpiderMiddleware(HttpProxyMiddleware):
def __init__(self, auth_encoding='latin-1', proxy_list_file=None):
if not proxy_list_file:
raise NotConfigured
self.auth_encoding = auth_encoding
# 分别用两个列表维护HTTP和HTTPS的代理
self.proxies = defaultdict(list)
# 从json文件中读取代理服务器信息,填入self.proxies
with open(proxy_list_file) as f:
proxy_list = json.load(f)
for proxy in proxy_list:
scheme = proxy['proxy_scheme']
url = proxy['proxy']
self.proxies[scheme].append(self._get_proxy(url,scheme))
@classmethod
def from_crawler(cls, crawler):
# 从配置文件中读取用户验证信息编码
auth_encoding = crawler.settings.get('HTTPPROXY_AUTH_ENCODING', 'latin-1')
# 从配置文件中读取代理服务器列表文件(json)的路径
proxy_list_file = crawler.settings.get('HTTPPROXY_PROXY_LIST_FILE')
return cls(auth_encoding, proxy_list_file)
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
def _set_proxy(self, request, scheme):
# 随机选择一个代理
creds, proxy = random.choice(self.proxies[scheme])
request.meta['proxy'] = proxy
if creds:
request.headers['Proxy-Authorization'] = b'Basic' + creds

不足之处

  • 数据量非常大,大概有10w多条,由于使用没有进行分布式处理也没有挂在服务器上跑,导致电脑被占用了一个下午,心痛。
  • 随机代理无法自动删除无效代理,无法重新下载失败链接,导致失败链接丢失,影响下载质量。
  • 豆瓣页面明明写的20本书!但是部分页面实际只有19本!骗子!过分!!哼!!!