我们曾经在过猪八戒,图片之家,BOSS直聘等网站,利用网站官方提供的搜索功能,搜索指定关键词的内容,并把这些内容都抓取下来。现在我们来总结一下这些网站信息的共同点。
1. 利用浏览器访问指定网站,并在官方的搜索栏中搜索想要的内容信息。 2. 服务器返回一系列页面列表,每个页面内包含着若干条被搜索内容的简介信息。 3. 逐条点击这些简介信息的网页链接就可以获得该条信息的详情内容。 4. 对服务器返回的每个页面列表均执行步骤3,直到页面列表全都遍历完成。
上图描述了全站数据抓取的基本模型,这个是个可以应用于很多网站,需求场景的通用模型,因此Scrapy专门针对这种情况专门写了一个全站数据抓取的案例,使我们只需要去关注简介信息,详情信息,页面列表翻页按钮这些每个网站不同的东西,而无需再编写这套通用的抓取数据业务逻辑的控制。总之,Scrapy想尽办法把通用的功能抽象出来只写一次,不通用的地方留出接口供用户自己实现。
本节使用汽车之家二手车页面来介绍如何进行全站数据抓取。
这部分内容之前做过,直接开干。
scrapy startproject qiche # 创建汽车项目 cd qiche # 进入qiche项目根目录 scrapy genspider ershouche che168.com # 创建ershouche爬虫
import scrapy from scrapy.linkextractors import LinkExtractor class ErshoucheSpider(scrapy.Spider): name = 'ershouche' allowed_domains = ['che168.com', 'autohome.com.cn'] start_urls = ['https://www.che168.com/china/list/#pvareaid=110965'] def parse(self, resp): print(resp.url) print(resp.xpath('//title/text()').extract_first().strip()) # 获取页面标题 # hrefs = resp.xpath('//ul[@class="viewlist_ul"]/li/a/@href').extract() # for href in hrefs: # # print(href) # 打印下看看 # yield scrapy.Request( # url=resp.urljoin(href), # callback=self.parse_detail # ) # scrapy 还提供了链接提取器的东西,也可以帮我们提取到页面中的超链接 le = LinkExtractor(restrict_xpaths=('//ul[@class="viewlist_ul"]/li/a',)) links = le.extract_links(resp) # 提取链接 for link in links: # print(link) # 打印下看看 # print(link.text.replace(" ","").strip()) # 打印下看看 # print(link.url) # 打印下看看 yield scrapy.Request( url=link.url, callback=self.parse_detail ) # 开始分页 page_le = LinkExtractor(restrict_xpaths=("//div[@id='listpagination']/a",)) pages = page_le.extract_links(resp) for page in pages: yield scrapy.Request( url=page.url, # 重复的url没关系. scrapy自动的帮我们完成去除重复 # dont_filter=True, # 不过滤 - 直接扔队列 别乱加 callback=self.parse # 下一页的内容和当前页一致. 然后数据解析的过程和当前页一致 ) def parse_detail(self, resp): try: print(resp.url) print(resp.xpath('//title/text()').extract_first().strip()) # 获取页面标题 except Exception as e: # 有的是手机页面,不对 print(resp.url) print("上面的URL报错了")
scrapy genspider -t ershou che168.com # 创建ershou CrawlSpider爬虫
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule class ErshouSpider(CrawlSpider): # CrawlSpider也继承了Spider,所以从根本上讲,ErshouSpider依然是一个Spider name = 'ershou' allowed_domains = ['che168.com', 'autohome.com.cn'] start_urls = ['https://www.che168.com/china/list/#pvareaid=110965'] rules = ( # rule 规则,这里定义了一堆规则,要求必须是元组或者列表 # Rule: 规则对象 Rule(LinkExtractor(restrict_xpaths=('//ul[@class="viewlist_ul"]/li/a')), callback='parse_item', follow=False), Rule(LinkExtractor(restrict_xpaths=("//div[@id='listpagination']/a")), follow=True), ) # 这里不能自己写parse, parse由crawlSpider提供 def parse_item(self, resp): # 处理页面详情 if "topicm.che168.com" not in resp.url: try: title = resp.xpath("//h3[@class='car-brand-name']/text()").extract_first() price = resp.xpath("//span[@id='overlayPrice']/text()").extract_first() if not title: title = resp.xpath("//h3[@class='car-brand-name']/i[@class='icon-cxc']/text()").extract_first() if not price: price = resp.xpath("//div[@class='goodstartmoney']/text()").extract_first() title = title.replace(" ","").strip() price = price.replace(" ","").strip() print(title, price) with open("title_price.txt", mode="a", encoding="utf-8") as f: f.write(f"{title},{price},{resp.url}\n") except Exception as e: print(f"{resp.url} 出错了") print(e) with open("error.txt", mode="a", encoding="utf-8") as f: f.write(f"{resp.url} 出错了\n") f.write(f"{e}\n")
使用Scrapy中的CrawlSpider模板可以帮我们快速的抓取全站数据,用起来很方便。
redis作为一款目前这个星球上性能最高的非关系型数据库之一。拥有每秒近十万次的读写能力,其实力只能用恐怖来形容。
redis是我见过这个星球上最好安装的软件了。比起前面的那一坨。它简直了…
直接把压缩包解压。然后配置一下环境变量就可以了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZVHKZDpf-1644061687934)(C:\Users\Apphao\Downloads\IT学习\爬虫课件\樵夫老师第一期\腾讯课堂VIP爬虫课件\11_直播第十一天-CrawlSpider和redis\课件\image-20210810184227132.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hmClu86N-1644061687935)(C:\Users\Apphao\Downloads\IT学习\爬虫课件\樵夫老师第一期\腾讯课堂VIP爬虫课件\11_直播第十一天-CrawlSpider和redis\课件\image-20210810184318301.png)]
接下来, 在环境变量中将该文件夹配置到path中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dcQdmVF7-1644061687935)(C:\Users\Apphao\Downloads\IT学习\爬虫课件\樵夫老师第一期\腾讯课堂VIP爬虫课件\11_直播第十一天-CrawlSpider和redis\课件\image-20210810184649037.png)]
我们给redis多配置几个东西(修改redis的配置文件, mac是: redis.conf, windows是: )
关闭bind
# bind 127.0.0.1 ::1 # 注释掉它
关闭保护模式 windows不用设置
protected-mode no # 设置为no
设置密码
requirepass 123456 # 设置密码
将redis怼到windows服务必须进入到redis目录后才可以
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wwGphxxP-1644061687935)(C:\Users\Apphao\Downloads\IT学习\爬虫课件\樵夫老师第一期\腾讯课堂VIP爬虫课件\11_直播第十一天-CrawlSpider和redis\课件\image-20210810185306517.png)]
# 将redis安装到windows服务 redis-server.exe --service-install redis.windows.conf --loglevel verbose # 卸载服务: redis-server --service-uninstall # 开启服务: redis-server --service-start # 停止服务: redis-server --service-stop
使用redis-cli链接redis
redis-cli -h ip地址 -p 端口 --raw # raw可以让redis显示出中文 auth 密码 # 如果有密码可以这样来登录, 如果没有.不用这一步
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sqc9TNVh-1644061687936)(C:\Users\Apphao\Downloads\IT学习\爬虫课件\樵夫老师第一期\腾讯课堂VIP爬虫课件\11_直播第十一天-CrawlSpider和redis\课件\image-20210810185605290.png)]
附赠RDM, redis desktop manager。可以帮我们完成redis数据库的可视化操作(需要就装, 不需要就算)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ouFrRdKH-1644061687936)(C:\Users\Apphao\Downloads\IT学习\爬虫课件\樵夫老师第一期\腾讯课堂VIP爬虫课件\11_直播第十一天-CrawlSpider和redis\课件\image-20210810185659813.png)]
redis中常见的数据类型有5个。
命令规则: 命令 key 参数
字符串(它自己认为是字符串, 我认为是任何东西。), redis最基础的数据类型。
常用命令
set key value # 添加一条数据 get key # 查看一条数据 incr key # 让该key对应的数据自增1(原子性, 安全) incrby key count # 让该key对应的value自增 count type key # 查看数据类型(set进去的东西一律全是字符串)
例如
set name zhangsan # 添加数据 name = zhangsan get name # 查看数据 zhangsan set age 10 get age # 10 incr age # 11 get age # 11 incrby age 5 # 16
哈希, 相当于字典。
常见操作
hset key k1 v1 # 将k1, v1存储在key上 hget key k1 # 将key上的k1提取出来 hmset key k1 v1 k2 v2 k3 v3.... # 一次性将多个k,v存储在key hmget key k1 k2....# 一次性将key中的k1, k2...提取出来 hgetall key # 一次性将key中所有内容全部提取 hkeys key # 将key中所有的k全部提取 hvals key # 将key中所有的v全部提取
示例:
HMSET stu id 1 name sylar age 18 HMGET stu name age # syalr 18 HGETALL stu # id 1 name sylar age 18 HKEYS stu # id name age HVALS stu # 1 syalr 18
列表, 底层是一个双向链表。可以从左边和右边进行插入。记住每次插入都要记得这货是个双向链表
常见操作
LPUSH key 数据1 数据2 数据3.... # 从左边插入数据 RPUSH key 数据1 数据2 数据3.... # 从右边插入数据 LRANGE key start stop # 从start到stop提取数据. LLEN key # 返回key对应列表的长度 LPOP key # 从左边删除一个.并返回被删除元素 RPOP key # 从右边删除一个.并返回被删除元素
示例:
LPUSH banji yiban erban sanban siban LRANGE banji 0 -1 # yiban erban sanban siban RPUSH ban ban1 ban2 ban3 LRANGE ban 0 -1 # ban1 ban2 ban3 LPOP ban # ban1 LLEN key # 2
set是无序的超大集合。无序, 不重复。
常见操作
SADD key 值 # 向集合内存入数据 SMEMBERS key # 查看集合内所有元素 SCARD key # 查看key中元素的个数 SISMEMBER key val # 查看key中是否包含val SUNION key1 key2 # 并集 SDIFF key1 key2 # 差集合, 在key1中, 但不在key2中的数据 SINTER key1 key2 # 计算交集, 在key1和key2中都出现了的 SPOP key # 随机从key中删除一个数据 SRANDMEMBER key count # 随机从key中查询count个数据
实例:
SADD stars 柯震东 吴亦凡 张默 房祖名 # 4 SADD stars 吴亦凡 # 0. 重复的数据是存储不进去的. SMEMBERS stars # 柯震东 吴亦凡 张默 房祖名 SISMEMBER stars 吴亦凡 # 吴亦凡在 stars里么? 1 在 0 不在 SADD my 周杰伦 吴亦凡 房祖名 SINTER stars my # 计算交集 吴亦凡 房祖名 SPOP my # 随机删除一个 SRANDMEMEBER my 2 # 从集合总随机查看2个
有序集合, 有序集合中的内容也是不可以重复的。并且存储的数据也是redis最基础的string数据。但是在存储数据的同时还增加了一个score。表示分值。redis就是通过这个score作为排序的规则的。
常用操作
ZADD key s1 m1 s2 m2 ... # 向key中存入 m1 m2 分数分别为s1 s2 ZRANGE key start stop [withscores] # 查看从start 到stop中的所有数据 [是否要分数] ZREVRANGE key start stop # 倒叙查看start到stop的数据 ZCARD key # 查看zset的数据个数 ZCOUNT key min max # 查看分数在min和max之间的数据量 ZINCRBY key score member # 将key中member的分值score ZSCORE key m # 查看key中m的分值
示例:
ZADD fam 1 sylar 2 alex 3 tory # 添加三个数据 ZRANGE fam 0 -1 WITHSCORES # 正序查看 ZREVRANGE fam 0 -1 WITHSCORES # 倒叙查看 ZINCRBY fam 10 alex # 给alex加10分 ZADD fam 100 alex # 给alex修改分数为100分 ZSCORE fam alex # 查看alex的分数 ZCARD fam # 查看fam的数据个数
redis还有非常非常多的操作。我们就不一一列举了。各位可以在网络上找到非常多的资料。
各位大佬们注意。数据保存完一定要save一下, 避免数据没有写入硬盘而产生的数据丢失
python处理redis使用专用的redis模块。同样的, 它也是一个第三方库.
pip install redis
获取连接(1)
from redis import Redis red = Redis(host="127.0.0.1", # 地址 port=6379, # 端口 db=0, # 数据库 password=123456, # 密码 decode_responses=True) # 是否自动解码
获取连接(2)
pool = redis.ConnectionPool( host="127.0.0.1", # 地址 port=6379, # 端口 db=0, # 数据库 password=123456, # 密码 decode_responses=True ) r = redis.Redis(connection_pool=pool) print(r.keys())
我们以一个免费代理IP池能用到的操作来尝试一下redis
# 存入数据 red.set("sylar", "邱彦涛") # 获取数据 print(red.get("sylar")) lst = ["张三丰", "张无忌", "张翠山", "张娜拉"] red.lpush("names", *lst) # 将所有的名字都存入names # # 查询所有数据 result = red.lrange("names", 0, -1) print(result) # 从上面的操作上可以看出. python中的redis和redis-cli中的操作是几乎一样的 # 接下来, 咱们站在一个代理IP池的角度来分析各个功能 # 抓取到了IP. 保存入库 red.zadd("proxy", {"192.168.1.1": 10, "192.168.1.2": 10}) red.zadd("proxy", {"192.168.1.3": 10, "192.168.1.6": 10}) red.zadd("proxy", {"192.168.1.4": 10, "192.168.1.7": 10}) red.zadd("proxy", {"192.168.1.5": 10, "192.168.1.8": 10}) # 给某一个ip增加到100分 red.zadd("proxy", {"192.168.1.4": 100}) # 给"192.168.1.4" 扣10分 red.zincrby("proxy", -10, "192.168.1.4") # 分扣没了. 删除掉它 red.zrem("proxy", "192.168.1.4") # 可用的代理数量 c = red.zcard("proxy") print(c) # 根据分值进行查询(0~100)之间 r = red.zrangebyscore("proxy", 0, 100) print(r) # 查询前100个数据(分页查询) r = red.zrevrange('proxy', 0, 100) # 判断proxy是否存在, 如果是None就是不存在 r = red.zscore("proxy", "192.168.1.4") print(r)