一、python、PHP、Java、C/C++爬虫的比较
答:
python:代码简洁,开发效率高,第三方模块多,相关的HTTP请求模块HTML解析模块非常丰富,拥有强大的爬虫框架scrapy以及成熟高效的scrapy-redis分布式策略,调用其他接口非常方便(胶水语言)
PHP:对多线程异步支持不够好,处理并发能力弱,爬虫是工具性程序,对速度、效率要求很高
Java:网络爬虫生态圈很完善,是python爬虫的最大对手。但是缺点是Java语言本身很笨重,代码量很大,任何修改都会导致代码的大量改动,而爬虫经常会因为网站的修改而修改采集代码
C/C++:运行效率和性能 很强,但是学习成本太高,代码成型比较慢
二、python爬虫基础知识
1.抓取HTML页面
HTTP请求的处理,urllib,urllib2,requests
处理后的请求可以模拟浏览器,获取服务器响应的文件
2.解析响应页面的内容
使用某种面属性语言来给需要提取的数据,定义一个匹配规则,符合这个规则的数据就会被匹配
re、xpath、beautifulSoup、jsonpath、pyquery等
3.采集动态HTML
动态页面采集:Selenium自动化测试工具+chromedriver模拟浏览器行为获取数据;模拟真实浏览器加载js、ajax等非静态页面采集
selenium
使用selenium关闭浏览器:
- driver.close():关闭当前的页面。
- driver.quit():关闭整个浏览器。
selenium定位元素:
- find_element_by_id:根据id来查找某个元素。
- find_element_by_class_name:根据类名查找元素。
- find_element_by_name:根据name属性的值来查找元素。
- find_element_by_tag_name:根据标签名来查找元素。
- find_element_by_xpath:根据xpath语法来获取元素。
- find_element_by_css_selector:根据css选择器选择元素。
要注意,find_element是获取第一个满足条件的元素。find_elements是获取所有满足条件的元素。
selenium表单操作:
- webelement.send_keys:给输入框填充内容。
- webelement.click:点击。
- 操作select标签:需要首先用
from selenium.webdriver.support.ui import Select
来包装一下选中的对象,才能进行select
选择:- select_by_index:按索引进行选择。
- select_by_value:按值进行选择。
- select_by_visible_text:按照可见文本进行选择。
selenium行为链:
有时候在页面中的操作可能要有很多步,那么这时候可以使用鼠标行为链类selenium.webdriver.common.action_chains.ActionChains来完成。比如现在要将鼠标移动到某个元素上并执行点击事件。那么示例代码如下:
inputTag = driver.find_element_by_id('kw')
submitTag = driver.find_element_by_id('su')
actions = ActionChains(driver)
actions.move_to_element(inputTag)
actions.send_keys_to_element(inputTag,'python')
actions.move_to_element(submitTag)
actions.click(submitTag)
actions.perform()
更多的鼠标相关的操作。
click_and_hold(element):点击但不松开鼠标。
context_click(element):右键点击。
double_click(element):双击。
操作cookie
-
获取所有的cookie:
for cookie in driver.get_cookies(): print(cookie)
-
根据cookie的key获取value:
value = driver.get_cookie(key)
-
删除所有的cookie:
driver.delete_all_cookies()
-
删除某个cookie:
driver.delete_cookie(key)
-
添加cookie:
driver.add_cookie({“name”:”username”,”value”:”abc”})
隐式等待和显示等待
-
隐式等待:指定一个时间,在这个时间内一直会处于等待状态。隐式等待需要使用
driver.implicitly_wait
。 -
显式等待:指定在某个时间内,如果某个条件满足了,那么就不会再等待,如果在指定的时间内条件都不满足,那么就不会再等待了。显式等待用的方法是
from selenium.webdriver.support.ui import WebDriverWait
。示例代码如下:driver.get("https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc") WebDriverWait(driver,100).until( EC.text_to_be_present_in_element_value((By.ID,"fromStationText"),"长沙") ) WebDriverWait(driver,100).until( EC.text_to_be_present_in_element_value((By.ID,"toStationText"),"北京") ) btn = driver.find_element_by_id("query_ticket") btn.click()
打开新窗口和切换页面:
-
selenium中没有专门的打开新窗口的方法,是通过
window.execute_script()
来执行js
脚本的形式来打开新窗口的。window.execute_script("window.open('https://www.douban.com/')")
-
打开新的窗口后
driver
当前的页面依然还是之前的,如果想要获取新的窗口的源代码,那么就必须先切换过去。示例代码如下:window.switch_to.window(driver.window_handlers[1])
4.验证码处理
tesseract:机器学习库,机器学习图像识别系统,处理简单的验证码
复杂验证码可以手动输入或者找专门的打码平台
5.scrapy框架(scrapy、pyspider)
高定制性、高性能(异步网络框架twitsted),所以数据下载速度非常快,高并发,提供了数据存储、数据下载、提取规则等组件
安装scrapy框架
pip install scrapy
scrapy框架架构
- Scrapy Engine(引擎):Scrapy框架的核心部分。负责在Spider和ItemPipeline、Downloader、Scheduler中间通信、传递数据等。
- Spider(爬虫):发送需要爬取的链接给引擎,最后引擎把其他模块请求回来的数据再发送给爬虫,爬虫就去解析想要的数据。这个部分是我们开发者自己写的,因为要爬取哪些链接,页面中的哪些数据是需要的,都是由程序员自己决定。
- Scheduler(调度器):负责接收引擎发送过来的请求,并按照一定的方式进行排列和整理,负责调度请求的顺序等。
- Downloader(下载器):负责接收引擎传过来的下载请求,然后去网络上下载对应的数据再交还给引擎。
- Item Pipeline(管道):负责将Spider(爬虫)传递过来的数据进行保存。具体保存在哪里,应该看开发者自己的需求。
- Downloader Middlewares(下载中间件):可以扩展下载器和引擎之间通信功能的中间件。
- Spider Middlewares(Spider中间件):可以扩展引擎和爬虫之间通信功能的中间件。
项目文件作用
- settings.py`:用来配置爬虫的。
middlewares.py
:用来定义中间件。items.py
:用来提前定义好需要下载的数据字段。pipelines.py
:用来保存数据。scrapy.cfg
:用来配置项目的
CrawlSpider爬虫
- 作用:可以定义规则,让Scrapy自动的去爬取我们想要的链接。而不必跟Spider类一样,手动的yield Request。
- 创建:scrapy genspider -t crawl [爬虫名] [域名]
- 提取的两个类:
- LinkExtrator:用来定义需要爬取的url规则。
- Rule:用来定义这个url爬取后的处理方式,比如是否需要跟进,是否需要执行回调函数等。
使用twisted异步保存MySQL
- 使用twisted.enterprise.adbapi来创建一个连接对象:
def __init__(self,mysql_config):
self.dbpool = adbapi.ConnectionPool(
mysql_config['DRIVER'],
host=mysql_config['HOST'],
port=mysql_config['PORT'],
user=mysql_config['USER'],
password=mysql_config['PASSWORD'],
db=mysql_config['DATABASE'],
charset='utf8'
)
@classmethod
def from_crawler(cls,crawler):
# 只要重写了from_crawler方法,那么以后创建对象的时候,就会调用这个方法来获取pipline对象
mysql_config = crawler.settings['MYSQL_CONFIG']
return cls(mysql_config)
- 在插入数据的函数中,使用
runInteraction
来运行真正执行sql语句的函数。示例代码如下:
def process_item(self, item, spider):
# runInteraction中除了传运行sql的函数,还可以传递参数给回调函数使用
result = self.dbpool.runInteraction(self.insert_item,item)
# 如果出现了错误,会执行self.insert_error函数
result.addErrback(self.insert_error)
return item
def insert_item(self,cursor,item):
sql = "insert into article(id,title,author,pub_time,content,origin) values(null,%s,%s,%s,%s,%s)"
args = (item['title'],item['author'],item['pub_time'],item['content'],item['origin'])
cursor.execute(sql,args)
def insert_error(self,failure):
print("="*30)
print(failure)
print("="*30)
下载器中间件
下载器中间件是引擎和下载器之间通信的中间件。在这个中间件中我们可以设置代理、更换请求头等来达到反反爬虫的目的。要写下载器中间件,可以在下载器中实现两个方法。一个是process_request(self,request,spider),这个方法是在请求发送之前会执行,还有一个是process_response(self,request,response,spider),这个方法是数据下载到引擎之前执行。
- process_request(self,request,spider)方法:
这个方法是下载器在发送请求之前会执行的。一般可以在这个里面设置随机代理ip等。- 参数:
- request:发送请求的request对象。
- spider:发送请求的spider对象。
- 返回值:
- 返回None:如果返回None,Scrapy将继续处理该request,执行其他中间件中的相应方法,直到合适的下载器处理函数被调用。
- 返回Response对象:Scrapy将不会调用任何其他的process_request方法,将直接返回这个response对象。已经激活的中间件的process_response()方法则会在每个response返回时被调用。
- 返回Request对象:不再使用之前的request对象去下载数据,而是根据现在返回的request对象返回数据。
- 如果这个方法中抛出了异常,则会调用process_exception方法。
- 参数:
- process_response(self,request,response,spider)方法:
这个是下载器下载的数据到引擎中间会执行的方法。- 参数:
- request:request对象。
- response:被处理的response对象。
- spider:spider对象。
- 返回值:
- 返回Response对象:会将这个新的response对象传给其他中间件,最终传给爬虫。
- 返回Request对象:下载器链被切断,返回的request会重新被下载器调度下载。
- 如果抛出一个异常,那么调用request的errback方法,如果没有指定这个方法,那么会抛出一个异常。
- 参数:
scrapy中设置代理
-
设置普通代理:
class IPProxyDownloadMiddleware(object): PROXIES = [ "5.196.189.50:8080", ] def process_request(self,request,spider): proxy = random.choice(self.PROXIES) print('被选中的代理:%s' % proxy) request.meta['proxy'] = "http://" + proxy
-
设置独享代理:
class IPProxyDownloadMiddleware(object): def process_request(self,request,spider): proxy = '121.199.6.124:16816' user_password = "970138074:rcdj35xx" request.meta['proxy'] = proxy # bytes b64_user_password = base64.b64encode(user_password.encode('utf-8')) request.headers['Proxy-Authorization'] = 'Basic ' + b64_user_password.decode('utf-8')
6.分布式策略
scrapy-redis,在scrapy基础上添加了一套以redis数据库为核心的组件,让scrapy框架支持分布式的功能,主要在redis里做请求去重、请求分配、数据临时存储等
redis配置
-
在ubuntu上安装redis:sudo apt install redis-server
-
连接reids服务器:redis-cli -h [ip地址] -p [端口号]
-
在其他电脑上连接本机的redis服务器:在/etc/redis/redis.conf中,修改bind,把redis服务器的ip地址加进去。示例如下:
bind 192.168.175.129 127.0.0.1
-
vim:有可能没有。那么通过sudo apt install vim就可以安装了。
-
虚拟机安装:vmware+ubuntu16.04.iso来安装。安装的时候,设置root用户的密码,用
useradd
命令来创建一个普通用户。后期方便通过xshell来连接。ubuntu不允许外面直接用root用户链接,那么我们可以先用普通用户连接,然后再切换到root用户
爬虫部署
-
在服务器上安装scrapyd:
pip3 install scrapyd
。 -
从
/usr/local/lib/python3.5/dist-packages/scrapyd
下拷贝出default_scrapyd.conf
放到/etc/scrapyd/scrapyd.conf
。 -
修改
/etc/scrapyd/scrapyd.conf
中的bind_address
为自己的IP地址。 -
重新安装
twisted
:pip uninstall twisted pip install twisted==18.9.0
如果这一步不做,后期会出现intxxx的错误。
-
在开发机上(自己的window电脑上)安装
pip install scrapyd-client
。 -
修改
python/Script/scrapyd-deploy
为scrapyd-deploy.py
-
在项目中,找到
scrapy.cfg
,然后配置如下:[settings] default = lianjia.settings [deploy] # 下面这个url要取消注释 url = http://服务器的IP地址:6800/ project = lianjia
-
在项目所在的路径执行命令生成版本号并上传爬虫代码:
scrapyd-deploy
。如果一次性想要把代码上传到多个服务器,那么可以修改scrapy.cfg
为如下:[settings] default = lianjia.settings [deploy:服务器1] # 下面这个url要取消注释 url = http://服务器1的IP地址:6800/ project = lianjia [deploy:服务器2] # 下面这个url要取消注释 url = http://服务器2的IP地址:6800/ project = lianjia
然后使用
scrapyd-deploy -a
就可以全部上传了。 -
curl for windows下载地址:
https://curl.haxx.se/windows/
,解压后双击打开bin/curl.exe即可在cmd中使用了。 -
在cmd中使用命令运行爬虫:
curl http://服务器IP地址:6800/schedule.json -d project=lianjia -d spider=house
-
如果后期修改了爬虫的代码,那么需要重新部署,然后服务器的scrapyd服务重新启动一下。
7.爬虫、反爬虫、反反爬虫之间的斗争
user-agent、代理、、验证码、动态页面加载、加密数据
三、爬虫中需要了解的知识点
1.网页相关
HTTP与HTTPS
HTTP协议:超文本传输协议,是一种发布和接收HTML页面的方法,端口:80
HTTPS协议:HTTP的安全版,在HTTP下价SSL层。端口:443
SSL层(安全套接层):主要用于web的安全传输协议,在传输层对网络连接进行加密,保障在Internet上数据传输安全。
HTTP工作原理
网络爬虫抓取过程:模拟浏览器操作的过程
浏览器主要功能是向服务器发出请求,在浏览器窗口中展示你选择的网络资源,HTTP是一套计算机通过网络进行通信的规则。
HTTP的请求与响应
HTTP通信:客户端请求信息、服务器响应信息
浏览器发送HTTP请求的过程:
- 在浏览器输入URL后,浏览器会向HTTP服务器发送HTTP请求,请求主要是’GET’和‘POST’两种
- 浏览器发送一个request请求去获取网站的HTML文件,服务器吧response文件对象发送回给浏览器
- 浏览器分析response中的HTML,发现其引用文件,浏览器再次发送request获取该内容
- 所有文件下载成功后,网页根据HTML语法结构,完整显示出来
GET和POST的区别
Get:请求的URL会附带参数
Post:请求的URL不会带参数
对于Get请求:查询参数在QueryString里保存
对于Post请求:查询参数在Form表单中保存,通常会用一个换行进行分割
URL基本格式
scheme://host[:port#]/path/…/?query-string
scheme:协议
host:服务器的ip地址或者域名
port#:服务器的端口
path:访问资源的路径
query-string:参数,发送给HTTP夫妻的数据
anchor:锚,跳转到网页的指定锚点位置
客户端HTTP请求
请求行、请求头、空行、请求数据
Get:从服务器上获取数据;请求参数显示在URL地址上
Post:从服务器获取数据的同时,向服务器传送数据
cookie和session
cookie:通过在客户端记录的信息确认用户的身份
session:通过在服务器端记录的信息确认用户的身份
2.urllib库
urlencode()对中文进行转码 unquote()解码
import urllib.parse
urlencode() # 转码,接收的是字典
unquote() # 解码
urllib.request构建请求
import urllib.request
url = 'http://www.baidu.com/s'
headers = {'User-Agenturlencode()': 'Mozilla...'}
keyword = input('请输入需要查询的字符串')
wd = {'wd': keyword}
# 通过urllib.parse.urlencode() 参数是一个dict类型
wd = urllib.parse.urlencode(wd)
# 拼接完整的url字符串
fullurl = url + '?' + wd
# 构造请求对象
request = urllib.request.Request(fullurl, headers=headers)
response = urllib.request.urlopen(request)
3.代理
免费代理网站
西刺免费代理IP https://www.xicidaili.com/
快代理 https://www.kuaidaili.com/free/inha/
IP海 http://www.iphai.com/free/ng
代理的使用:{代理类型,ip与端口}
{‘HTTP’: ‘218.241.219.226:9999’}
将代理IP放在系统环境变量中:cd ~/.bash_profile中
加入:
proxyuser = '用户名'
export proxyuser
proxypassword = '密码'
export proxypassword
在命令行窗口,输入source ~/.bash_profile,使系统环境变量生效
在自己的代码中:
import os
proxyuser = os.environ.get('proxyuesr')
proxypassword = os.environ.get('proxypassword')
将用户名和密码与ip拼接起来
3.页面解析和数据提取
正则表达式
python中的re模块游两种方式:
pattern = re.compile(’\d’)将正则表达式编译成一个pattern规则对象
m = pattern.match()从开始位置查找,找到第一个就返回,值匹配一次
pattern.search()从任何位置查找,找到第一个就返回,只匹配一次
pattern.findall()所有的全部匹配,返回列表
pattern.finditer()所有的全匹配,返回的是一个迭代器
pattern.split()分割字符串,返回列表
pattern.sub()替换
m.group()取出匹配的内容
re.I 表示忽略大小写
re.S 表示全文匹配
XML与HTML的区别
XML:可扩展标记语言,被设计为传输和存储数据,其焦点是数据的内容
HTML:超文本标记语言,显示数据以及如何更好的显示数据
HTML DOM:文档对象模型,通过HTML DOM,可以访问所有的HTML元素,联通他们所包含的文本和属性,可以对其中的内容进行修改和删除,也可以创建新的元素
CSS选择器:BeautifulSoup4
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
四大对象种类:Tag、NavigableString、BeautifulSoup、Comment
1)Tag:标签
soup.title
soup.head
2)NavigableString:获取标签内部的文字 .string
soup.p.string
3)BeautifulSoup:文档的内容,相当于特殊的Tag
4)Comment:特殊类型的 NavigableString 对象,输出内容没有注释的内容
JSON与JSONPATH
JSON:JavaScript中的对象和数组。对象{},数组[]
import json
json.loads() # 将json格式转换为python格式
json.dumps() # 将python类型转化为json字符串,返回一个str对象
json.dump() # 将python内置类型序列化为json后写入文件
json.load() # 将json类型序列化为python后写入文件