爬虫笔记,记录本此爬虫过程中遇到的问题和解决方法

本次案例用的的技术点主要所有:python3,MySQL,scrapy,requests库,faker库
全篇文章提到的标点均为英文状态下的标点符号。需要中文时会标注。

数据库篇

1. 数据库字段类型及长度

问题1. 提前设计表

爬虫之前先设计好了数据表,方便爬取到的数据直接存入数据库,但是如果数据库字段的类型或长度出现问题,第一时间可能就会认为是爬虫的哪个步骤出现了问题,因而浪费时间,因此一定要确定数据库表的字段之后在进行爬取,会方便很多。

2. 创建数据表

在创建数据表时,表名不是用单引号括起来的而是用反引号
单引号:'
反引号:在键盘上的位置位于tab键上方,esc键下方。

同理,如果数据表的字段名中,有与mysql关键字冲突的字段名,可用反引号括起来,就不会冲突了。

2. 数据库可视化工具:navicat

不要在数据表有数据的时候更改数据表(设计表)!!

问题1. 谨慎使用设计表

如果不小心更改了数据表字段,导致出现

Deadlock found when trying to get lock try restarting transaction

翻译过来就是:尝试获取锁时发现死锁尝试重新启动事务
解决方法:
这个时候不要在操作数据表了,直接在服务中找到mysql相关服务,重启服务即可恢复。

pymysql篇

python连接MySQL遇到的问题。

问题1. sql语句中的占位符

向mysql中插入数据,编写sql语句时,例如

sql = "insert into pds_review values(%s,%s,%s,%s)"

其中,无论哪个字段在数据表中是什么数据类型,统一都用%s作为占位符,但是插入的数据还是要遵守数据表中的数据类型。

问题2. mysql插入数据时出现Incorrect string value: ‘\xF0\x9F…’ for column ‘name’ at row 1的异常

原因是UTF-8编码有可能是两个、三个、四个字节。Emoji表情或者某些特殊字符是4个字节,而MySQL的utf8编码最多3个字节,所以数据插不进去。


解决方法

  1. 修改msyql数据库目录下的my.ini文件
[mysql]
default-character-set=utf8mb4

character-set-server=utf8mb4

2. 修改数据表的编码 ~~~sql ALTER TABLE `table` DEFAULT CHARACTER SET utf8mb4; ~~~
3. 修改单个字段的编码 ~~~sql ALTER TABLE `tablename` CHANGE `字段名1` `字段名2` VARCHAR(36) CHARACTER SET utf8mb4 NOT NULL; ~~~ (修改所有字段的编码) ~~~sql alter table `tablename` convert to character set utf8mb4; ~~~

scrapy篇


1. scrapy shell 设置参数

在启动scrapy shell 时设置请求头,代理ip参数可通过在url会添加-s,在之后添加需要的参数,例如添加请求头:

scrapy shell "https://www.whzj.com" -s USER_AGENT='请求头'

2. 工作流程

spider

主要文件,爬虫程序,内部有多个方法,方法之间的调用实现了数据的请求。
通过yield关键字和方法回调实现了异步爬取,从而大大提高小路。

Item 和 Pipeline

爬虫获取到数据会存储在Item中,Item是一个python类,存储爬取的各个数据,在items.py文件中编写。

Pipeline是一个管道文件,爬虫爬到的数据通过Item传输到Pipeline中,Pipeline对数据进行处理,包括数据的去重,存入数据库等操作。
Pipeline在pipelines.py文件中编写,其中可以写多个Pipeline,用于不同的爬虫程序(scrapy项目中可以创建多个爬虫程序)。
process_item()方法是必须实现的方法,用来处理数据,接收爬虫文件传输过来Item。

Middleware

中间件,作用于请求发出与下载器之间,通过对请求的加工来实现更好的请求。
功能:设置请求头,设置代理IP等。实现反反爬虫。

设置:setting.py

以上的文件都需要在配置文件中设置,并设置级别。

3. 一个项目中多个spider、item、pipeline的使用

运行多个爬虫
方法1:定义程序,集中启动

方法一比较简单。
在项目路径下创建crawl.py文件,内容如下:

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

process = CrawlerProcess(get_project_settings())

# myspd1是爬虫名
process.crawl('myspd1')
process.crawl('myspd2')
process.crawl('myspd3')

process.start()

方法2:修改crawl源码

在github中找到源码:
https://github.com/scrapy/scrapy/blob/master/scrapy/commands/crawl.py
在spiders目录的同级目录下创建mycmd目录,并在该目录中创建一个mycrawl.py,将crawl源码复制进来,修改其中的run方法,改为如下内容:

def run(self, args, opts):
    # 获取爬虫列表
    spd_loader_list = self.crawler_process.spider_loader.list()
    # 遍历各爬虫
    for spname in spd_loader_list or args:
        self.crawler_process.crawl(spname, **opts.spargs)
        print("此时启动的爬虫:" + spname)
    self.crawler_process.start()
使用多个Item

需要是所有item直接在items.py文件中创建即可,需要在spider中使用引用即可:

from mztr.items import StrItem

mztr是我的项目名。

传输到管道中时,判断传输过来的item是那个item:

if isinstance(item,doubanTextItem): 
	执行这个item的管道处理程序

使用多个pipeline

Pipeline.py文件中编写需要实现功能的pipeline。

方法1:指定spider的管道

setting.py中开启管道。
后面的数字是执行的优先级,数值越低优先值越高;只要未被注释的管道都会别执行,只是执行顺序不同而已。

DOWNLOADER_MIDDLEWARES = {
   'mztr.middlewares.MztrDownloaderMiddleware': 543,
   # 'mztr.middlewares.ProxyMiddleware': 543,
   # 'mztr.middlewares.RandomUserAgent': 2,
   # 'mztr.middlewares.RandomProxy': 100,
}

在spider中指定管道。

    custom_settings = {
        'ITEM_PIPELINES': {'mztr.pipelines.ProxyMiddleware': 300},
    }

方法2:管道中根据spider名字判断

在管道文件中判断spider需要拿个管道

class MymultispiderPipeline(object):
    def process_item(self, item, spider):
        if spider.name == 'myspd1':
            print('myspd1的pipelines')
        elif spider.name == 'myspd2':
            print('myspd2的pipelines')
        elif spider.name == 'myspd3':
            print('myspd3的pipelines')
        return item

spider.name可以获取到爬虫程序的名字。

4. 爬虫代码Spider

yield关键字

其中yield关键字可以大大提供爬虫的效率,建议使用,对比return关键字对yield大概的理解是:return关键字将结果返回,等返回程序执行完成后,在继续执行当前程序,而yield关键字则是,将结果发送给指定程序后,继续执行当前的程序,从而实现类似于异步的机制。

scrapy.Request()类的实例化

对于爬取普通网站,不需要验证码,不需要登入的界面,一般用scrapy.Request类直接去爬取信息就行,下面是Request类的定义:

class Request(object_ref):

    def __init__(self, url, callback=None, method='GET', headers=None, body=None,
                 cookies=None, meta=None, encoding='utf-8', priority=0,
                 dont_filter=False, errback=None, flags=None):
                 # url是要爬取的网址
                 # callback是回调函数
                 # method是请求的方式post还是get
                 # headers是浏览器伪装的头信息
                 # body是网页源代码信息
                 # cookies是登入某网站后,网站在你电脑上保留的信息
                 # meta要携带或者传递的信息
                 # encoding是编码方式
                 # priority用来设置访问网站的优先级
                 # dont_filter是否允许重复爬取网站

其中,url是必填的,其他参数是选填的。meta参数可以以字典的形式给回调的函数传输数据。dont_filter参数通常设置为TRUE。


程序入口:

def start_requests(self):
        # 首先爬一次登陆页,看是否有验证码。回调函数为parse()

request对象实例化后,得到一个response对象,response.url属性可以得到请求的url。

反爬虫篇


1. 设置请求头


通过`faker`库可以随机出相当多的数据,但是实测随机出的请求头是不能用的,这个问题一致困扰了好久,一直以为是爬虫的问题。改成自己浏览器的请求头就好了。 `fake_useragent`库同样是一个获得随机请求头的python库,不过相对于faker,它只能获得请求头。 我在用的时候经常出现问题,各种报错,所以没有再用。

2. 设置代理IP

这项没成功,因为网上找到的免费Ip大多数都不能用。
scrapy.Request()实例化时,在参数meta中设置proxy,设置为代理IP,可以通过代理IP发送请求。

测试代理IP是否有效

通过requests库使用代理IP给目标网站发送一个请求,观察返回的状态码和内容,可以测试出代理IP是否能用。

3. 项目设置

主要是设置setting.py文件
除非特殊需要,禁用cookies,防止某些网站根据Cookie来封锁爬虫。

COOKIES_ENABLED = False

设置下载延迟,太快会容易认定是爬虫,而被封。建议使用随机时长,时间太固定也容易被认定是爬虫。

DOWNLOAD_DELAY = random.ifrom(1,3)

4. 抓包

网站中有的数据不能在网站源码中找到,说明是动态加载的,涉及到了ajax技术,而爬虫只需要知道,怎么获取这些数据包就行。
通常,这些数据包都是以json格式传输,在网站的页面打开检查工具,Network选项中选中XHR只看数据包,在左侧数据包列表中,查找自己想要的数据包,在header位置找到请求的url,修改参数请求获取想要的json数据包。


数据解析篇

解析json数据:在json中快速找到想要的数据key,json在线视图查看器网站:
https://www.bejson.com/jsonviewernew
或者安装浏览器插件jsonview方便在浏览器页面中直观的查看器json数据结构

1. 遇到的问题

问题1:json中文乱码

由于爬虫存储数据库时,存在数据数据需要存储,因此先把数组转换成json格式在存入数据库。
在转换json时,出现了中文乱码问题。
解决方法:
给方法加上ensure_ascii=False参数,例如

t = json.dumps(r, ensure_ascii=False)  

json转换数组的方法是json.loads()

问题2:json转换dict(字典)错误

字典转换json的理解:将dict降级为字符串。
但是json转换dict(dict()强制转换的方式)时会报错误:

ValueError: dictionary update sequence element #0 has length 1; 2 is required

因此不能直接强制转换。
解决方法:
json.loads()方法,或者eval()方法
这两种方法不会报错。

json知识点

json中必须用双引号,否则会出错。

2. 请求与解析

用到的库:
lxml是一个python第三方库,需要安装。主要用的时lxml中的etree方法

请求与解析的总体流程:

  • 通过requests库发送get/post请求,得到一个response对象
  • 对resopnse对象进行初步解析.content.decode("utf8")得到字符串
  • 使用etree.HTML()方法解析得到的字符串,得到lxml.etree._Element对象
  • lxml.etree._Element对象xpath解析得到想要的内容

参考连接:

https://www.cnblogs.com/nmsghgnv/p/12369656.html
https://blog.csdn.net/vample/article/details/84955590
https://blog.csdn.net/xiaosongbk/article/details/65446351
https://blog.csdn.net/weixin_42345642/article/details/100972408
https://blog.csdn.net/qq_43546676/article/details/89043445
https://www.jianshu.com/p/6bd6869631d9
https://zhuanlan.zhihu.com/p/33816647

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值