学习爬虫第一步学习模拟登陆
只要是这里记住密码
登陆了才有退出
可以查看用户
那么它是如何知道你登录了,最重要的是cookie,其他值比如session,打开浏览器早就没了,application本地存储有一大堆东西
加密的数据,有些数据是base64编码后加密过再发过来的
现在把这个数据删除掉。下一次发送请求的时候,oscid就没了
现在就需要登录了
现在把登录后,cookie里的免登陆信息都保存,放在另一个浏览器信息里,再发起,应该可以识别,http是无状态协议,根本不知道你是哪个浏览器,有些浏览器就是按F10就双屛操作了,这时候等于两个浏览器,我们是user-agent号称,根本无法判断,只要有服务端注入的cookie信息就认你。
因为目前浏览器技术就是这么做的,突破不了,除非获取每个浏览器特别的一个id,但是这样能读到你计算机的id就没有安全可言了,所以浏览器运行脚本是很恐怖的事情,所以不能让脚本 什么都做,做BS编程基本被捆住手脚,怎么突破,就用xt5x,类似flash这样的插件来做这个事情。
flash插件也有暴露后门,后面还可以编程,也就是可以绕过浏览器限制,做一些它想要做的事情。
只要是免登陆就得用cookie
再次登录
先全部clearall
登录之后就有了
这些值在任何发的请求里都是有的
这个header信息应该保存下
现在开始写代码,这样会得到一个session类
直接session一个实例回来
session也有类似的
可以用with,把内容写进去就关了
header到此还没结束,是个字典
这里文本需要转换成字典,原来的GET和referer可以去掉
复制到postman里
现在kv都是一致的
点code,选择python,requests
现在就可以拷贝
记得把token删除掉
下面就稍微改改,要发起多次会话的话,下一次发起请求还要cookie,所以就用session,每一次发送get的时候也成
现在的代码几乎什么都不用做
执行一下得到的结果
文件里就记录到了
这样就搞定了
模拟登陆就搞定了,所以cookie的使用要很注意
凡是有记住密码再次登陆的基本就是这个原理,csrftoken是防止别人窃取身份,但是免密码登陆最真是没办法,是用cookie实现的,没辙。
有些网站为了避免用户嫌弃麻烦,降低了安全使用
其实没必要破解验证码,最爬虫是要考虑爬取什么内容,而不是攻破网站,你登录了账号,可以看需要爬取哪些内容
有了postman就可以搞定这些代码了,要把登录后的cookie拿来
不想用session,用requests也可以
这个显示自己的名字还可以通过其他方式来看
登录之后有自己的页面
复制下这个地址
可以把登录页保存
如果现在去访问登录页面,就可以进行判断
直接进去了,也是一种判断方式,已经登录的,点击登录页就不需要登录了,这也是一种判断方式
没有登录页面,拿到的就是登录页面
再试试
刚才重新登录了很多次,刚才拿到的cookie和现在代码里的,现在相当于跨浏览器免密登录,照样登录成功
只要cookie是没有过期的,基本上可以继续使用
这个网站做了limit上限,这也是反爬的最有效手段
这次就用user-agent即可
第一步先爬取,在网页里定位XPath,css是很容易的,定位到这里,就可以获取每个的a标签链接
你再点击上一页,就看到网页,如果反复吧97,98加入,那就是重复爬取了,因为这些链接指向同一个页面,所以url要去重,应该用字典,因为还有爬取的时候需要打标记,所以不能用set
说到底就是kv,链接有没有爬取过,所以看到的爬虫框架无非就是做了非常复杂的kv存储,然后在这边做了所谓的去重
这边需要有个队列来存储爬取过的url,两个字典,已爬取,未爬取
如果将kv提出去,放到第三方数据库,就是redis
现在可以用拼接的方式
写个函数,看看url产生,start起始,end结束,每个网站都不同,所以每个网站都要写个模块,这样运行一下
这样就成了
这样就可以了
用这种方案就紧密耦合在一起了,如果要中间使用异步调用,中间只能使用queue了
用request方法访问这样返回一个url,
这里的n是相对路径
拷贝一个xpath
这个是不能用的,定死了
这个news_list就是我们想要的
这个news_entry就是我们想要的
在网页里写XPATH抓取试试
这两样是我们需要的
css是需要写,h2.news_entry 下面直接写a就可以提取到元素了
先把提取内容的代码写一下,用到解析,就需要用到beautifulsoup
用lxml效率更高,用xpath和css也可以,css是需要用到select,这样就拿到元素了,找完之后是一大堆东西,是个列表。
除了select也可以用find_all
我们打印出来刚好是字符串表达形式
attrs就可以看到里面的属性了,这个属性可以直接get
调整一下
现在拼接起来,把baseurl加上即可,就得到了新闻的url,下面就是新闻的title
写同一个文件,不需要同时写,我们写文件一般只要一个就够了,这么多产生数据该如何给,就天然想到队列,一个个来,这是由你消费者驱动生产,你消费者从里面拿一个,让生产者强行和消费者速度匹配了,最麻烦的地方在于分析,好歹需要把网页展开了对html进行分析,而产生url的速度很快,所以应该有个容器放这些数据,谁有空谁来拿
一个类只要得到存储url就够了,还有一个类只需要解析,各自做各自的事情,到后面再封装成类,管url就管url,管爬取就管爬取,管分析管分析,管存储管存储,是完全独立的,靠函数就可以解决。
现在写一个队列,但是我们后面会替换成第三方队列
开启函数就不停的运转,while true 需要把time.sleep也引进来,因为太慢了,把threading也引进来,使用event,实例化一下
用wait是可以控制速度,还可以用iset,这里要给别的网站做get请求,别的网站会分析你,太快了就认为是爬虫,
拿到的数据就是我们要存储下来的
写成这样,以后引入第三方queue 的类就可以直接用了。,这样就从url读取数据然后去一个个爬取,去循环,可以延迟1秒,然后上面就可以isset
get方法这个叫阻塞住
现在是用 一种非阻塞的方式,不然阻塞住的话,这个set就没有用,里面阻塞把set改了,还退不出来,1秒爬一次用这种方式就可以搞定了,现在只不过没拿到就抛出异常
在rabbitmq的时候,basic_get方法不阻塞,会返回三元组里面是三个none,所以我们在这里也使用不阻塞的方式。
不阻塞的方式有好处,event发生改变,一set,就可以退出来了
把所有内容打入到了outputs里,outputs拿到数据就准备往外写 了
用追加的方式a+,从outputs里拿一个data,这个data可以顺便就解构,outputs.get(True,1),这里写还是比较浪费时间的,写文件毕竟慢一点。每一秒拿一下,慢慢存,如果拿的很慢,可以拿到数据存到不同的文件,文件顺序无所谓,(文件标题+url)谁存在前后都是无区别的,所以可以开两个,都问outputs要数据,一部分存在了persist进程1里,另外放在persist2进程里,合起来才是整体
拿到数据后准备存储,直接解构存一下,加一下flush,不然好几行才出去
作为新闻标题一般不会出现title,最后换一行,但是用\t定义的存储数据多就不太合适了
写个文件测试一下,执行一下看看写了什么
但是字符1是在16进制31那里
其实这样写更好看点
这个字符一般不会有人用,做为分隔符,我们直接split就出来
如果文件太大需要滚动,建议用日志模块,日志文件,满了就生成一个新的出来
捕获数据和解析还在耦合着,也需要分离开,写一个解析函数parse,又一个队列,htmls,不要搞一个队列,在里面通过类型区分,效率太低,内存够就来三个。我们最后是要替换成第三方的
这里需要把text扔到htmls这个队列里
不用管bytes,text,在用这个的时候已经是unicode了
下面需要循环解析还是一样的套路,它处理的时间可能不止1 秒可以让它歇一会或者让他永久阻塞,但是最后针对没什么可处理了,就不需要永久阻塞了,我们用一个待阻塞待超时即可
**去拿数据然后解析,这个数据用什么方式写随便,只要正常提取都OK,拿到元素后,元素往往到跟元素,是元素对象,元素对象照样有attrs可以自己拿。
**
其实也可以省略,把这个变成字典,字典的风险稍微大点,这样就有可能是none,最好判断下,看它返回的是none,如果是none,此条信息就丢弃
没有href就直接丢掉了,然后判断是否拿到的是none
现在完成的,首先不断产生url,忙完了就从url里拿一条出来,发起get请求,获得返回的html,爬虫只做爬取的事情
parse看到html里有数据,get过来后立即分析,,分析之后存储,交给异步另一个队列去了
这个队列有人盯着,这里开几个根据你的设定来
到目前为止有了这几个函数后,多线程没有任何问题,这几个函数可以直接把多线程搞定,用多线程的方式可以直接来做这个事情
之前写在一起的函数,生成url到抓取html,到html分析,到存储写在一个函数里,现在是分开了,这个程序用queue是更好的选择,各忙 各的,而且可以使用多线程
不用多进程是这个不支持,multing processing里 的queue可以做这个,但是其实就是为了以后多进程做准备的,不管多线程和多进程,用第三方queue肯定没问题
格式化下代码
现在准备写线程函数,concurrent.futures,import ThreadPoolExecutor 线程执行器
建议多线程使用这个库,最大先来10个,线程池提交任务就跑起来了,只不过看结果要看future对象,future对象应该看down,直接拿result会阻塞。
然后就要开始爬了crawler,下面是分析parse,最后持久化persist
这些都是异步的,所以写个event.is_set,但是程序分离就没必要了,因为分离里就是不同进程。
下面是打印所有活着的,采用这种方式可以观察下
这个是比较简单的,都不要future对象
这里能用的真不多,submit,往里面提交就完了
最后把里面都用queue代替,一个一个queue全部提取出来
产生1到1就是1个页面,然后爬取crawler,然后分析parse,分析完存储persist
打印看一下,执行一下
线程池一旦创建后,没有必要把已经有的线程干掉,下回准备复用,现在最大是10个,是用了4个就4个了
但是现在没有数据,查看问题
4个线程有了,重新执行一下
按照道理crawler是可以拿到数据的
这里需要传参,刚才路径没给
就写了一条
这里记得加下 while not event.is_set,这样就可以反复拿数据,只要拿数据有问题就是没拿到,就抛出异常,异常不关心
点就是01
执行一把
现在改个两页的,一页是30,2页就是60
正好60条
这种打印可以去除
现在跑还是跟单线程没什么区别,但是下面改动一下,生成url启动后,下面启动4个crawler,起5个parse,下面开一个线程存储即可,这就是多线程爬虫