python爬虫:urllib 、Scrapy 、PhantomJS 、分布式爬虫

python爬虫:urllib & Scrapy

爬虫

想要了解爬虫,首先应该了解HTTP协议的实现细节,还应该稍微了解python中正则表达式和XPath表达式的语法。

爬取网页的方式可以分为广度优先BSF和深度优先DSF两种,通用的策略是以BSF的方式来爬取,但要限制其最大的深度
爬取的网页可以是静态网页也可以是AJAX的动态网页,也可以爬取web service

一、如何对海量的URL进行去重?
1.将URL直接存入数据库 —— 极不推荐,DB读写效率太低。
2.将URL存入HashSet —— 读取速度为O(1),但是占用内存空间。
3.将URL进行一次MD5/SHA-1单向Hash加密后,存入数据库/HashSet。
4.位图(Bit-Map)法:建立一个BitSet,将每一个URL经过一个Hash函数映射到某一位。但是使用该方法要求URL的基数非常大,否则的话可能会因为频繁的Hash碰撞而导致误判断,同时Hash算法也应该选好,降低碰撞概率
5.Bloom Filter(中文名为“布隆过滤器”):Bloom Filter的本质其实还是位图法,只不过它对同一个URL采用了3个不同的Hash函数进行映射,只要3个位置上有一个位置没有元素,就说明该URL没有重复,这样就可以大大降低碰撞概率。

二、如何对URL进行有效的记录?
1.网页数量大的情况下,可以通过BloomFilter进行压缩。
2.可以用Redis替代关系型数据库。


urllib模块

方法:

  1. urllib.request.urlretrive(url, filePath):直接下载网页到本地,以文件形式保存
  2. urllib.request.urlcleanup():清除缓存
  3. info():查看网页相应的简介信息
  4. getcode():输出网页爬取的HTTP状态码
  5. geturl():获取当前访问的网页的URL
  6. urllib.request.urlretrive(url, timeout):可以通过timeout设置超时时间。
  7. urllib.request.Request(posturl, postdata):发送POST请求,其中postdata可以通过urllib.parse.urlencode({"key1":"val1", "key2":"val2"}).encode("utf-8")进行构建

1.爬取HTML内容

通过urllib.request.urlopen('url').read()获取网页的二进制数据,然后通过.decode("utf-8")转为UTF-8格式。

1
2
3
4
response = urllib.request.urlopen("http://www.dragonbaby308.com")
html = response.read() # 得到二进制数据
html = html.decode("utf-8") # utf-8解码
print(html)

2.将爬取内容以文件形式保存

1
2
3
4
5
6
7
import urllib.request

response = urllib.request.urlopen("http://placekitten.com/g/500/600")
cat_img = response.read()
# with语句可以保证文件操作的安全性
with open('cat_500_600.jpg', 'wb') as f:
f.write(cat_img)

3.异常处理

爬虫异常处理模块:urllib.error
爬虫异常处理类:URLError及其子类HTTPError,其中HTTPError有异常状态码和异常原因。
URLError出现的原因:

  1. 连不上服务器
  2. 网络错误
  3. URL不存在
  4. 触发HTTPError
1
2
3
4
5
6
7
8
9
import urllib.request
import urllib.error
try:
urllib.request.urlopen("http://www.dragonbaby308.com``")
except urllib.error.URLError as e:
if hasattr(e,"code"):
print(e.code)
if hasattr(e,"reason"):
print(e.reason)

4.浏览器伪装

伪装request header中的User-Agent,如Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
修改方法有两种:

  1. 修改urllib.request.build_opener().addheaders属性后,通过urllib.request.build_opener().open("url").read()爬取;
  2. 通过urllib.request.Request()下的add_header()
1
2
3
4
5
6
7
8
9
10
11
12
13
import urllib.request

url = "http://www.dragonbaby308.com"
headers = ("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36")
opener = urllib.request.build_opener()
# 修改addheaders参数
opener.addheaders = [headers]
# 通过urllib.request.builde_opener().open("url").read爬取
# 也可以通过urllib.request.install_opener(opener)将opener安装为全局,此后就可以直接通过urllib.request.urlopen()进行爬取了
data = opener.open(url).read()
fh = open("useragent.html", "wb")
fh.write(data)
fh.close()

用户代理池(User-Agent Pool)

将多个用户代理User-Agent构成一个池,每次爬取时,从池中随机选择一个用户代理。

IP代理池


Scrapy爬虫框架

Scrapy爬虫框架是一个为了爬取网站数据、提取结构性数据而编写的应用框架,最初是为了页面抓取所设计,也可以应用于获取API的返回数据或通用的网络爬虫。

用途

可以应用于数据挖掘信息处理存储历史数据……


安装

1.安装pip

pip是类似于maven的版本控制和项目构建工具,默认是安装好的,通过pip --version查看版本,我的版本是pip 19.0.3 from c:\users\dragonbaby308\appdata\local\programs\python\python37-32\lib\site-packages\pip (python 3.7)
可能会提示你通过python -m pip install --upgrade pip更新到最新版本19.1.1

2.通过pip安装wheel

pip install wheel,可以在命令行输入wheel测试,如果有输出则为成功

3.通过pip安装lxml

pip install lxml

4.通过pip安装OpenSSL

pip install pyOpenSSL

5.通过pip安装twisted

前往非官方网站下载twistedCtrl + Ftwisted,我选择的是Twisted‑19.2.1‑cp37‑cp37m‑win32.whl,可能要翻墙。下载完成后,通过pip install命令安装即可,如我的是pip install C:\Users\DragonBaby308\Desktop\Twisted-19.2.1-cp37-cp37m-win32.whl

6.通过pip安装Scrapy

pip install Scrapy即可。
如果之前的步骤没有完成,可能会报错error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": https://visualstudio.microsoft.com/downloads/

7.通过pip安装pypiwin32

如果在后续使用Scrapy的过程中,出现ModuleNotFoundError: No module named 'win32api'的错误的话,通过pip install pypiwin32安装pypiwin32模块即可。


架构

scrapy

  1. Scrapy Engine:框架核心,控制数据流在系统所有组件之间的流动;
  2. Downloader:下载器,获取页面的数据并提供给Spiders,数据是从Scheduler调度器获得的;
  3. Scheduler:调度器,从Scrapy Engine接收request数据,本质是任务队列;
  4. Spiders:用户编写的用于分析抓取的数据的爬虫,提取出Items交给Item Pipline进行持久化;
  5. Items:是用于保存爬取目标的容器,使用方法类似于字典,并且提供了额外保护机制避免拼写错位导致的未定义字段错误

常用指令

主要分为全局指令和项目指令。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 任何指令后加-h都可以查看命令文档
scrapy command -h

# ------------------------------------------------------------------------------

# 全局指令

# 测试硬件性能,初步估计每分钟可以爬取多少个页面
bench Run quick benchmark test

# 直接获取某个网页,并显示网页爬取过程
# 如:scrapy fetch http://www.baidu.com
fetch Fetch a URL using the Scrapy downloader

# 通过预先定义的模板生成爬虫文件
# 通过 scrapy genspider -l 可以显示当前系统可用的爬虫模板,默认有:
# basic
# crawl 主要用于自动爬虫
# csvfeed 主要用于爬取CSV格式的文件和数据
# xmlfeed 主要用于爬取XML格式的文件和数据
# 如:scrapy genspider -t basic db3 dragonbaby308.com —— 模板为basic,爬虫名db3,允许爬取的域为dragonbaby308.com
genspider Generate new spider using pre-defined templates

# 不创建Scrapy项目,直接运行爬虫文件
# 如:scrapy runspider BaiduSpider.py
runspider Run a self-contained spider (without creating a project)

# 查看配置信息,即settings.py
# 如:scrapy settings --get BOT_NAME
settings Get settings values

# 以Shell模式进行爬取,在此模式下可以交互式输入python代码
# 如:scrapy shell http://www.baidu.com
# 输入exit()命令即可退出shell
shell Interactive scraping console

# 创建项目
startproject Create new project

# Scapy版本
version Print Scrapy version

# 爬取一个网页,并直接在浏览器上打开爬取后的网页
# 如:scrapy view http://www.baidu.com
view Open URL in browser, as seen by Scrapy

# ------------------------------------------------------------------------------

# 项目指令

# 以合同的方式,测试爬虫是否符合规范
# 如:scrapy check db3
check Check spider contracts

# 运行某一个爬虫文件
# 如:scrapy crawl db3
# 通过加入--nolog可用隐藏日志,直接显示结果,如:scrapy crawl db3 --nolog
crawl Run a spider

# 调用编辑器编写某个爬虫文件
edit Edit spider

# 列出当前项目下所有可用的爬虫文件
list List available spiders

# 解析URL并打印结果
parse Parse URL (using its spider) and print the results

使用

使用Scrapy抓取网站分为4步。

1.创建Scrapy项目

cd到对应目录下,scrapy startproject ProjectName创建项目。
创建好后项目结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
ProjectName
scrapy.cfg #配置文件
ProjectName
__pycache__
__init__.py
items.py #Items是用于保存爬取到的数据的容器,使用方法类似于字典,并且提供了额外保护机制避免拼写错位导致的未定义字段错误
middlewares.py
pipelines.py #对应Items Pipline,用于爬取数据的处理
settings.py #设置
spiders #存放爬虫的文件夹
__pycache__
__init__.py
2.定义Item容器(item.py)

编写爬虫项目,首先第一步当然是确认爬取目标,而Items就是用于保存爬取目标的容器,使用方法类似于字典,并且提供了额外保护机制避免拼写错位导致的未定义字段错误。
修改item.py,以name = scrapy.Field()的形式定义爬取目标:

1
2
3
4
5
6
7
8
9
10
11
12
import scrapy

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

# 商品名
title = scrapy.Field()
# 商品链接
link = scrapy.Field()
# 商品评论
comment = scrapy.Field()
3.编写爬虫(spiders)

首先通过scrapy genspider -t basic spiderName allowed_domains创建爬虫,然后使用编辑器进行编写,如我们使用scrapy genspider -t basic dangdang dangdang.com创建一个当当网商品爬虫:

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
# -*- coding: utf-8 -*-
import scrapy
# 导入Items容器,使用Items
from ..items import TutorialItem
# 循环爬取
from scrapy.http import Request

class DangdangSpider(scrapy.Spider):
name = 'dangdang'
allowed_domains = ['dangdang.com']
# 从第1页开始爬取
start_urls = ['http://category.dangdang.com/pg1-cid4005291.html']

def parse(self, response):
item = TutorialItem()
# 信息的提取:爬取之的所有信息都在response中
# 观察特点:所有<a name="itemlist-picture">中的title就是商品名称
item["title"] = response.xpath("//a[@name='itemlist-picture']/@title").extract()
# 观察特点:所有<a name="itemlist-picture">中的href就是商品链接
item["link"] = response.xpath("//a[@name='itemlist-picture']/@href").extract()
# 观察特点:所有<a name="itemlist-review">内的内容即为“x条评论”
item["comment"] = response.xpath("//a[@name='itemlist-review']/text()").extract()

# print(item["title"])
# 提交数据
yield item
# 循环爬取,进行翻页
for i in range(2, 81):
url = 'http://category.dangdang.com/pg'+ str(i) +'-cid4005291.html'
yield Request(url,callback = self.parse)

需要注意的是,我们要将配置文件settings.py中的ROBOTSTXT_OBEY属性从True修改为False,不遵守机器人法则,否则会被拦截下来,无法爬取成功。
最后我们通过scrapy crawl dangdang --nolog运行爬虫,测试结果如下:

当当网爬虫

4.储存内容(pipelines.py)

首先,要在配置文件settings.py中解除piplines的注释,然后将SomePipeline修改为你的pipelines.py文件中对应的类:

1
2
3
ITEM_PIPELINES = {
'tutorial.pipelines.TutorialPipeline': 300,
}

然后修改pipelines.py

1
2
3
4
5
6
7
8
9
class TutorialPipeline(object):
def process_item(self, item, spider):
'pipelines核心处理函数'
for i in range(0, len(item["title"]) ):
title = item["title"][i]
link = item["link"][i]
comment = item["comment"][i]
print(title+":"+link+":"+comment)
return item

测试结果如图:

piplines

MySQL数据库保存
  1. 安装pymysql模块,用于操作MySQL数据库:pip install pymysql
  2. 数据库建表:
1
2
3
create database dangdang;
use dangdang;
create table AdultProducts(id int(32) auto_increment primary key, title varchar(100), link varchar(100) unique, comment varchar(100));
  1. 修改piplines.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pymysql

class TutorialPipeline(object):
def process_item(self, item, spider):
'pipelines核心处理函数'
# 建立连接
conn = pymysql.connect(host="127.0.0.1",user="root",passwd="dogsonII2!",db="dangdang",charset="utf8")
for i in range(0, len(item["title"]) ):
title = item["title"][i]
link = item["link"][i]
comment = item["comment"][i]
sql = "insert into AdultProducts(title,link,comment) values('" + title + "','" + link+"','" + comment + "')";
try:
conn.query(sql)
except Exception as e:
print(e)
conn.close()
return item

Selenium & PhantomJS 应用于爬虫

PhantomJS的特定

  1. 本质:PhantomJS本质上是一个没有界面的浏览器
  2. 缺点:使用PhantomJS来做爬虫,效率会远低于urllibScrapy
  3. 优点:由于PhantomJS本质上是一个浏览器,所以它可用解决很多反爬虫措施,比如异步JS/Cookie/随机混淆……
  4. 使用:首先反爬虫部分使用PhantomJS,再使用urllibScrapy处理拿到的数据

操作

1.下载PhantomJS,并配置环境变量

下载PhantomJS后解压,就可以直接运行其bin目录下的phantomjs.exe了。
如果需要使用命令行,那么就需要将其bin目录加入环境变量中,具体操作与Java相似。
在命令行输入phantomjs如果有反应,则说明安装成功。

2.安装自动化测试工具Selenium

pip install selenium==2.48.0,值得注意的是,高版本的Selenium不再支持PhantomJS了 - - |||

3.使用PhantomJS(以操作www.baidu.com为例)

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
import time
# web引擎,用于调用PhantomJS
from selenium import webdriver

# 建立浏览器
browser = webdriver.PhantomJS()
# 发送get请求
browser.get("https://www.baidu.com")
# 截图
browser.get_screenshot_as_file("D://python/baidu.png")
# 找元素:browser.find_element_by_xpath('...')
# .clear() 清除输入
# .send_keys('...') 输入数据
# .click() 点击
browser.find_element_by_xpath('//*[@id="kw"]').clear()
browser.find_element_by_xpath('//*[@id="kw"]').send_keys('吴莹莹')
browser.find_element_by_xpath('//*[@id="su"]').click()
# PhantomJS从本质上来说就是浏览器操作,所以需要加上一个延迟,等待数据处理完成
time.sleep(5)
browser.get_screenshot_as_file("D://python/baidu_wyy.png")

# 得到页面源代码
data = browser.page_source

# 退出PhantomJS
browser.quit()

# 对数据进行处理

结果如下:
pjs


分布式爬虫

常见架构

多台机器(可以是真实机器,也可以是虚拟机,还可以是Docker容器镜像) + 爬虫(如urllib/Scrapy) + 任务共享中心(一般采用Redis实现)

常用技术

  1. Docker + Redis + urllib + (MySQL)
  2. Docker + Redis + Scrapy + Scrapy-Redis + (MySQL)

可以参考《Docker基础》《Redis基础》


Chrome爬虫插件

  1. EditThisCookie:EditThisCookie是一个cookie管理器。您可以添加,删除,编辑,搜索,锁定和屏蔽cookies!
  2. XPath Helper:实时显示XPath匹配的数目和对应的位置,方便检查语句是否正确。
  3. Toggle JavaScript:检测当前网页哪些元素是通过AJAX动态加载的。
-------------本文结束感谢您的阅读-------------

本文标题:python爬虫:urllib 、Scrapy 、PhantomJS 、分布式爬虫

文章作者:DragonBaby308

发布时间:2019年06月29日 - 21:29

最后更新:2020年01月25日 - 13:13

原始链接:http://www.dragonbaby308.com/python-spider/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%