爬虫:request、scrapy、scrapy-redis的爬虫流程,匹配机制:xpath、正则、css选择器,反爬虫策略

文章目录

  • 一、python、PHP、Java、C/C++爬虫的比较
  • 二、python爬虫基础知识
      • 1.抓取HTML页面
      • 2.解析响应页面的内容
      • 3.采集动态HTML
            • selenium
            • 操作cookie
            • 隐式等待和显示等待
            • 打开新窗口和切换页面:
      • 4.验证码处理
      • 5.scrapy框架(scrapy、pyspider)
            • 安装scrapy框架
            • scrapy框架架构
            • 项目文件作用
            • CrawlSpider爬虫
            • 使用twisted异步保存MySQL
            • 下载器中间件
            • scrapy中设置代理
      • 6.分布式策略
            • redis配置
            • 爬虫部署
      • 7.爬虫、反爬虫、反反爬虫之间的斗争
  • 三、爬虫中需要了解的知识点
      • 1.网页相关
            • HTTP与HTTPS
            • HTTP工作原理
            • HTTP的请求与响应
            • GET和POST的区别
            • URL基本格式
            • 客户端HTTP请求
            • cookie和session
      • 2.urllib库
            • urlencode()对中文进行转码 unquote()解码
            • urllib.request构建请求
      • 3.代理
            • 免费代理网站
        • 3.页面解析和数据提取
            • 正则表达式
            • XML与HTML的区别
            • CSS选择器:BeautifulSoup4
            • JSON与JSONPATH

一、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关闭浏览器:

  1. driver.close():关闭当前的页面。
  2. driver.quit():关闭整个浏览器。

selenium定位元素:

  1. find_element_by_id:根据id来查找某个元素。
  2. find_element_by_class_name:根据类名查找元素。
  3. find_element_by_name:根据name属性的值来查找元素。
  4. find_element_by_tag_name:根据标签名来查找元素。
  5. find_element_by_xpath:根据xpath语法来获取元素。
  6. find_element_by_css_selector:根据css选择器选择元素。
    要注意,find_element是获取第一个满足条件的元素。find_elements是获取所有满足条件的元素。

selenium表单操作:

  1. webelement.send_keys:给输入框填充内容。
  2. webelement.click:点击。
  3. 操作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
  1. 获取所有的cookie:

    for cookie in driver.get_cookies():
        print(cookie)
    
  2. 根据cookie的key获取value:

    value = driver.get_cookie(key)
    
  3. 删除所有的cookie:

    driver.delete_all_cookies()
    
  4. 删除某个cookie:

    driver.delete_cookie(key)
    
  5. 添加cookie:

    driver.add_cookie({“name”:”username”,”value”:”abc”})
    
隐式等待和显示等待
  1. 隐式等待:指定一个时间,在这个时间内一直会处于等待状态。隐式等待需要使用driver.implicitly_wait

  2. 显式等待:指定在某个时间内,如果某个条件满足了,那么就不会再等待,如果在指定的时间内条件都不满足,那么就不会再等待了。显式等待用的方法是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()
    
打开新窗口和切换页面:
  1. selenium中没有专门的打开新窗口的方法,是通过window.execute_script()来执行js脚本的形式来打开新窗口的。

    window.execute_script("window.open('https://www.douban.com/')")
    
  2. 打开新的窗口后driver当前的页面依然还是之前的,如果想要获取新的窗口的源代码,那么就必须先切换过去。示例代码如下:

    window.switch_to.window(driver.window_handlers[1])
    

4.验证码处理

tesseract:机器学习库,机器学习图像识别系统,处理简单的验证码
复杂验证码可以手动输入或者找专门的打码平台

5.scrapy框架(scrapy、pyspider)

高定制性、高性能(异步网络框架twitsted),所以数据下载速度非常快,高并发,提供了数据存储、数据下载、提取规则等组件

安装scrapy框架

pip install scrapy

scrapy框架架构
  1. Scrapy Engine(引擎):Scrapy框架的核心部分。负责在Spider和ItemPipeline、Downloader、Scheduler中间通信、传递数据等。
  2. Spider(爬虫):发送需要爬取的链接给引擎,最后引擎把其他模块请求回来的数据再发送给爬虫,爬虫就去解析想要的数据。这个部分是我们开发者自己写的,因为要爬取哪些链接,页面中的哪些数据是需要的,都是由程序员自己决定。
  3. Scheduler(调度器):负责接收引擎发送过来的请求,并按照一定的方式进行排列和整理,负责调度请求的顺序等。
  4. Downloader(下载器):负责接收引擎传过来的下载请求,然后去网络上下载对应的数据再交还给引擎。
  5. Item Pipeline(管道):负责将Spider(爬虫)传递过来的数据进行保存。具体保存在哪里,应该看开发者自己的需求。
  6. Downloader Middlewares(下载中间件):可以扩展下载器和引擎之间通信功能的中间件。
  7. Spider Middlewares(Spider中间件):可以扩展引擎和爬虫之间通信功能的中间件。
项目文件作用
  1. settings.py`:用来配置爬虫的。
  2. middlewares.py:用来定义中间件。
  3. items.py:用来提前定义好需要下载的数据字段。
  4. pipelines.py:用来保存数据。
  5. scrapy.cfg:用来配置项目的
CrawlSpider爬虫
  1. 作用:可以定义规则,让Scrapy自动的去爬取我们想要的链接。而不必跟Spider类一样,手动的yield Request。
  2. 创建:scrapy genspider -t crawl [爬虫名] [域名]
  3. 提取的两个类:
    • LinkExtrator:用来定义需要爬取的url规则。
    • Rule:用来定义这个url爬取后的处理方式,比如是否需要跟进,是否需要执行回调函数等。
使用twisted异步保存MySQL
  1. 使用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)
  1. 在插入数据的函数中,使用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),这个方法是数据下载到引擎之前执行。

  1. process_request(self,request,spider)方法:
    这个方法是下载器在发送请求之前会执行的。一般可以在这个里面设置随机代理ip等。
    1. 参数:
      • request:发送请求的request对象。
      • spider:发送请求的spider对象。
    2. 返回值:
      • 返回None:如果返回None,Scrapy将继续处理该request,执行其他中间件中的相应方法,直到合适的下载器处理函数被调用。
      • 返回Response对象:Scrapy将不会调用任何其他的process_request方法,将直接返回这个response对象。已经激活的中间件的process_response()方法则会在每个response返回时被调用。
      • 返回Request对象:不再使用之前的request对象去下载数据,而是根据现在返回的request对象返回数据。
      • 如果这个方法中抛出了异常,则会调用process_exception方法。
  2. process_response(self,request,response,spider)方法:
    这个是下载器下载的数据到引擎中间会执行的方法。
    1. 参数:
      • request:request对象。
      • response:被处理的response对象。
      • spider:spider对象。
    2. 返回值:
      • 返回Response对象:会将这个新的response对象传给其他中间件,最终传给爬虫。
      • 返回Request对象:下载器链被切断,返回的request会重新被下载器调度下载。
      • 如果抛出一个异常,那么调用request的errback方法,如果没有指定这个方法,那么会抛出一个异常。
scrapy中设置代理
  1. 设置普通代理:

    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
    
  2. 设置独享代理:

    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配置
  1. 在ubuntu上安装redis:sudo apt install redis-server

  2. 连接reids服务器:redis-cli -h [ip地址] -p [端口号]

  3. 在其他电脑上连接本机的redis服务器:在/etc/redis/redis.conf中,修改bind,把redis服务器的ip地址加进去。示例如下:

    bind 192.168.175.129 127.0.0.1
    
  4. vim:有可能没有。那么通过sudo apt install vim就可以安装了。

  5. 虚拟机安装:vmware+ubuntu16.04.iso来安装。安装的时候,设置root用户的密码,用useradd命令来创建一个普通用户。后期方便通过xshell来连接。ubuntu不允许外面直接用root用户链接,那么我们可以先用普通用户连接,然后再切换到root用户

爬虫部署
  1. 在服务器上安装scrapyd:pip3 install scrapyd

  2. /usr/local/lib/python3.5/dist-packages/scrapyd下拷贝出default_scrapyd.conf放到/etc/scrapyd/scrapyd.conf

  3. 修改/etc/scrapyd/scrapyd.conf中的bind_address为自己的IP地址。

  4. 重新安装twisted

    pip uninstall twisted
    pip install twisted==18.9.0
    

    如果这一步不做,后期会出现intxxx的错误。

  5. 在开发机上(自己的window电脑上)安装pip install scrapyd-client

  6. 修改python/Script/scrapyd-deployscrapyd-deploy.py

  7. 在项目中,找到scrapy.cfg,然后配置如下:

    [settings]
    default = lianjia.settings
    
    [deploy]
    # 下面这个url要取消注释
    url = http://服务器的IP地址:6800/
    project = lianjia
    
  8. 在项目所在的路径执行命令生成版本号并上传爬虫代码: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就可以全部上传了。

  9. curl for windows下载地址:https://curl.haxx.se/windows/,解压后双击打开bin/curl.exe即可在cmd中使用了。

  10. 在cmd中使用命令运行爬虫:

    curl http://服务器IP地址:6800/schedule.json -d project=lianjia -d spider=house
    
  11. 如果后期修改了爬虫的代码,那么需要重新部署,然后服务器的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后写入文件

你可能感兴趣的:(python,网络,中间件,python,js)