Python 爬虫20题精讲丨【生长吧!Python】

爬虫

编程题

1.请使用正则(regular expression module)模块're'从一段中英文的字符串取得所有电话号码,其中电话号码可变,s="This is our Chinese homepage.我们公司的客服电话:02-2511-6530.若要购买商品编号为:05-1423,请来电0928837577."

  import re
  s="This is our Chinese homepage.我们公司的客服电话:02-2511-6530.若要购买商品编号为:05-1423,请来电0928837577."
  new_s = re.sub(r"-","",s)
  phone_num_list = re.findall(r'\d{10}', new_s)
  print(phone_num_list)
2.如何用python抓取京东网站某个类品下商品的图片,写上思路,关键点和对应的
反爬策略有可能有变化,现在是能跑通的
# https://www.jd.com/
# https://search.jd.com/Search?keyword=iphone%E5%90%88%E7%BA%A6%E6%9C%BA&enc=utf-8&wq=iphone%E5%90%88%E7%BA%A6%E6%9C%BA&pvid=9585617222944822b7039b975c89c7f1
# https://search.jd.com/Search?keyword=iphone%E5%90%88%E7%BA%A6%E6%9C%BA&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=iphone%E5%90%88%E7%BA%A6%E6%9C%BA&page=3&s=53&click=0

# https://search.jd.com/Search?keyword=iphone%E5%90%88%E7%BA%A6%E6%9C%BA&enc=utf-8&wq=iphone%E5%90%88%E7%BA%A6%E6%9C%BA&page=1  3
"""
http://list.jd.com/list.html?cat=9987,653,655
http://list.jd.com/list.html?cat=9987,653,655&page=2
<div id = "plist"
class ="goods-list-v2 J-goods-list gl-type-3 " >
<div class ="page clearfix" >
"""
import re
import urllib.request


def craw(url, page):
    html1 = urllib.request.urlopen(url).read()
    # fhandle = open('D:/爬虫/抓取文件/'+"jingdong1106"+str(page)+".html", "wb")
    # fhandle.write(html1)
    # fhandle.close()
    html1 = str(html1)
    pat1 = '<div id="plist".+? <div class="page clearfix">'
    result1 = re.compile(pat1).findall(html1)
    result1 = str(result1)
    # result1=result1[0]
    pat2 = '<img width="220" height="220" data-img="1" src="//(.+?\.jpg)">'
    # pat3 = '<img width="220" height="220" data-img="1" data-lazy-img="done" src="//(.+?\.jpg)">'
    pat4 = '<img width="220" height="220" data-img="1" data-lazy-img="//(.+?\.jpg)">'

    imagelist = re.compile(pat2).findall(result1)
    # imagelist3 = re.compile(pat3).findall(result1)
    imagelist4 = re.compile(pat4).findall(result1)
    #  imagelist=imagelist+imagelist3+imagelist4  #pat2 和pat3是一样的,去掉其中一个数量还是不变
    imagelist = imagelist + imagelist4  # pat2 和pat3是一样的,去掉其中一个数量还是不变
    x = 1
    end = "/"
    for imageurl in imagelist:

        try:
            string2 = imageurl[imageurl.rfind(end):]  # 在imageurl中查找最后一个正斜杠/后面的字符,图片名称
            pat2 = '/(.+?\.jpg)'  # 提取最后一个斜杠到 .jpg后缀名称
            imagelist2 = re.compile(pat2).findall(string2)
            imagelist2 = str(imagelist2[0])  # 提取第一个名称 ,去掉正斜杠
            imagename = "文件命名" + imagelist2
            imageurl = "http://" + imageurl
            print(imagename)
            urllib.request.urlretrieve(imageurl, filename=imagename)
        except urllib.request.URLError as e:
            if hasattr(e, "code"):
                x += 1
            if hasattr(e, "reason"):
                x += 1
        x += 1


def catchnameAndPrice(url, page):
    html1 = urllib.request.urlopen(url).read()
    html1 = str(html1)
    pat1 = '<div id="plist".+? <div class="page clearfix">'
    result1 = re.compile(pat1).findall(html1)
    result1 = str(result1)
    # result1=result1[0]
    pat2 = '<em>.+? </em>'
    imagelist = re.compile(pat2).findall(result1)
    for imageurl in imagelist:
        print(imageurl)
        pat1 = '<em>\\n(.+?\.</em>)'
        result2 = re.compile(pat1).findall(imageurl)
        result1 = str(result2)
        print(result1)


for i in range(1, 2):
    url = 'https://list.jd.com/list.html?cat=9987,653,655&page=' + str(i)
    craw(url, i)
    # catchnameAndPrice(url,i)

理论简答题

1.简述一下爬虫的步骤
答案:
    1. 确定需求(人)
    2. 确定资源(人)
    3. 通过URL获取网站的返回信息(爬虫)
    4. 定位数据(爬虫)
    5. 存储数据(程序)
2.遇到反爬机制怎么处理
答案:
    反爬机制(headers):
      1. 判断User-Agent
      2. 判断Referer
      3. 判断Cookie
    如果以上操作还是没有成功爬取页面,就用将浏览器中的全部头信息放进来
    注意:如果把全部headers拿过来的话记得把Accept-Encoding: gzip, deflate注释掉
3.进程与线程的关系?
答案:
    进程如一个车间,线程如车间内的一条流水线;创建进程需要申请特别的内存空间(车间),各进程间是竞争关系,所以创建进程的开销大;而多个线程属于同一个进程    (车间),线程间共享进程中的资源,属于合作关系,所以创建线程开销小。所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线     程才是cpu上的执行单位.
4.遇到过哪些反爬的案例,分别是如何解决的(不限一种
答案:
    1.
    BAN USERAGENT很多的爬虫请求头就是默认的一些很明显的爬虫头python-requests/2.18.4,诸如此类,当运维人员发现携带有这类headers的数据包,直接拒绝访       问,返回403错误
    解决方法:直接r=requests.get(url,headers={‘User-Agent':'Baiduspider'})把爬虫请求headers伪装成百度爬虫或者其他浏览器头就行了。
    拓展: BAN REFERER
    西刺代理,今日头条

    2.
    BAN IP:网页的运维人员通过分析日志发现最近某一个IP访问量特别特别大,某一段时间内访问了无数次的网页,则运维人员判断此种访问行为并非正常人的行为,于     是直接在服务器上封杀了此人IP。
    解决方法:此种方法极其容易误伤其他正常用户,因为某一片区域的其他用户可能有着相同的IP,导致服务器少了许多正常用户的访问,所以一般运维人员不会通过此种     方法来限制爬虫。不过面对许多大量的访问,服务器还是会偶尔把该IP放入黑名单,过一段时间再将其放出来,但我们可以通过分布式爬虫以及购买代理IP也能很好的解     决,只不过爬虫的成本提高了。
    boss 直聘,伯乐在线

    3.
    BAN COOKIES:服务器对每一个访问网页的人都set-cookie,给其一个cookies,当该cookies访问超过某一个阀值时就BAN掉该COOKIE,过一段时间再放出来,当然一     般爬虫都是不带COOKIE进行访问的,可是网页上有一部分内容如新浪微博是需要用户登录才能查看更多内容。
    解决办法:控制访问速度,或者某些需要登录的如新浪微博,在某宝上买多个账号,生成多个cookies,在每一次访问时带上cookies
    需要登录才能访问的网站,拉勾网等

    4.
    验证码验证:当某一用户访问次数过多后,就自动让请求跳转到一个验证码页面,只有在输入正确的验证码之后才能继续访问网站
    解决办法:python可以通过一些第三方库如(pytesser,PIL)来对验证码进行处理,识别出正确的验证码,复杂的验证码可以通过机器学习让爬虫自动识别复杂验证码,     让程序自动识别验证码并自动输入验证码继续抓取
    微信公众号文章爬取,豆瓣登录等

    5.
    javascript渲染:网页开发者将重要信息放在网页中但不写入html标签中,而浏览器会自动渲染<script>标签中的js代码将信息展现在浏览器当中,而爬虫是不具备执     行js代码的能力,所以无法将js事件产生的信息读取出来
    解决办法:通过分析提取script中的js代码来通过正则匹配提取信息内容或通过webdriver+phantomjs直接进行无头浏览器渲染网页。
    今日头条,淘宝列表页面等

    6.
     ajax异步传输:访问网页的时候服务器将网页框架返回给客户端,在与客户端交互的过程中通过异步ajax技术传输数据包到客户端,呈现在网页上,爬虫直接抓取的话     信息为空
    解决办法:通过fiddler或是wireshark抓包分析ajax请求的界面,然后自己通过规律仿造服务器构造一个请求访问服务器得到返回的真实数据包。
    猎聘网,雪球网,手机版微博等
5.进程,线程和协程的定义以及区别?
答案:
    进程:
    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的单位。

    线程:
    线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资     源(如程序计数器,一组寄存器和栈),但是它可与同属于一个进程的其他的线程共享进程所拥有的全部资源。

    协程:
    协程是一种程序组件,是由子例程(过程、函数、例程、方法、子程序)的概念泛化而来的,子例程只有一个入口点且只返回一次,而协程允许多个入口点,可以在指定     位置挂起和恢复执行。

    区别:
    进程之间不共享任何状态,进程的调度由操作系统完成,每个进程都有自己的独立的内存空间,进程间的通讯主要是通过信号传递的方式来实现的,实现的方式有多种,     信号量,管道,事件等,任何一种方式的通讯效率都需要通过内核,导致通讯效率比较低。由于是独立的内存空间,上下文切换的时候需要保存先调用栈的信息,CPU各     寄存器的信息,虚拟内存,以及打开的相关句柄等信息,所以导致上下文进程间切换开销很大,通讯麻烦.

    线程之间共享变量,解决了通讯麻烦的问题,但对变量的访问需要锁,线程的调度主要也是有操作系统完成,一个进程可以拥有多个线程,但是其中每个线程会共享父进     程向操作系统申请资源,这个包括虚拟内存,文件等,由于是共享资源,所以创建线程所需要的系统资源占用比进程小很多,相应的可创建的线程数量也变得相对多很       多。线程时间的通讯除了可以使用进程之间通讯的方式之外还可以通过共享内存的方式进行通信,所以这个速度比通过内核要快很多。另外在调度方面也是由于内存是共     享的,所以上下文切换的时候需要保存的东西就相对少一些,这样一来上下文切换也变得高效

    协程的调度完全由用户控制,一个线程可以有多个协程,用户创建了几个线程,然后每个线程都是循环按照指定的任务清单顺序完成不同的任务,当任务被堵塞的时候执     行下一个任务,当恢复的时候再回来执行这个任务,任务之间的切换只需要保存每个任务的上下文内容,就像直接操作栈一样的,这样就完全没有内核切换的开销,可以     不加锁的访问全局变量,所以上下文的切换非常快;另外协程还需要保证是非堵塞的且没有相互依赖,协程基本上不能同步通讯,多采用异步的消息通讯,效率比较高。

    一个线程可以创建和销毁另一个线程,同一个进程中的多个线程之间可以并发执行。相对进程而言,线程是一个更加接近执行体的概念,它可以与同进程中的其他线程共     享数据,但拥有自己的栈空间,拥有独立的执行序列

    进程与线程的主要差别在于它们是不同的操作系统的资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程死掉就等     于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操     作,只能用线程,不能用进程。
    1)一个程序至少有一个进程,一个进程至少有一个线程。
    2)线程的划分尺度小于进程,使得多线程程序的并发性高。
    3)另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
    4)线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,     由应用程序提供多个线程执行控制。
    5)从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理     以及资源分配。这就是进程和线程的重要区别。

    线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时线程适合于在SMP机器上运行,而进程则可以跨机器迁移

    一般将进程定义为一个正在运行的程序的实例。我们在任务管理器重所看到的每一项,就可以理解为一个进程,每个进程都有一个地址空间,这个地址空间里有可执行文     件的代码和数据,以及线程堆栈等。一个程序至少有一个进程。进程可以创建子进程,创建的子进程可以和父进程一起工作,也可以独立运行。
    而线程是隶属于进程的,也就是说,线程是不能单独存在的,线程存在于进程中。每个进程至少有一个主线程,进程里的线程就负责执行进程里的代码,这也叫做进程       的“惰性”。线程所使用的资源是它所属的进程的资源。线程也有自己的资源,主要组成部分就是一些必要的计数器和线程栈,占用的资源很少。我们可以理解为进程就是     个容器,而线程才是真正干活的。线程可以在内核空间实现,也可以在用户空间实现。
6.Http与Https的区别是什么?
    •		HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
    •	    HTTP 是不安全的,而 HTTPS 是安全的
    •	    HTTP 标准端口是80 ,而 HTTPS 的标准端口是443
    •	    在OSI 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层
    •	    HTTP 无法加密,而HTTPS 对传输的数据进行加密
    •	    HTTP无需证书,而HTTPS 需要CA机构颁发的SSL证书
    •	HTTP全称是Hyper  Text  Transfer Protocol,中文全称为超文本;
    •	HTTPS全称是Hyper Text  Transfer Protocol over Secure Socket Layer,也就是说比HTTP多了安全层,通俗的讲来说就是HTTP的安全版;
7.常用的HTTP方法有哪些
答案:
    ★GET: 请求页面,并返回内容
    ★POST:大多用于提交表单或上传文件,数据包含在请求体中
    PUT: 传输文件,报文主体中包含文件内容,保存到对应URI位置。
    HEAD: 获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有效。
    DELETE:请求服务器删除指定的页面,与PUT方法相反
    CONNECT:把服务器当做跳板,让服务器代替客户端访问其他网页
    OPTIONS:循序客户端查看服务器的性能
    TRACE:回显服务器收到的请求,主要用于测试或诊断
8.说一说redis-scrapy中的redis的作用
    redis最主要的作用是起到了全局队列的作用。
9.遇到过得反爬虫策略以及解决方法
答案:
    1.通过headers反爬虫
    2.基于用户行为的发爬虫:(同一IP短时间内访问的频率)
    3.动态网页反爬虫(通过ajax请求数据,或者通过JavaScript生成)
    4.对部分数据进行加密处理的(数据是乱码)

    解决方法:
    1.对于基本网页的抓取可以自定义headers,添加headers的数据
    2.使用多个代理ip进行抓取或者设置抓取的频率降低一些,
    3.动态网页的可以使用selenium + phantomjs 进行抓取
    4.对部分数据进行加密的,可以使用selenium进行截图,使用python自带的pytesseract库进行识别,但是比较慢最直接的方法是找到加密的方法进行逆向推理。
10.urllib 和 urllib2 的区别

答案:
    urllib 和urllib2都是接受URL请求的相关模块,但是urllib2可以接受一个Request类的实例来设置URL请求的headers,urllib仅可以接受URL。
    urllib不可以伪装你的User-Agent字符串。
    urllib提供urlencode()方法用来GET查询字符串的产生,而urllib2没有。这是为何urllib常和urllib2一起使用的原因。
11.简述说明scrapy的基本机构流程
    1. 在创建好scrapy项目后,spider需要有初始的start_url 或者start_requests(),之后会在内部生成request到Engine;
    2. Engine将初始的Request发送给Scheduler,Engine也会从Scheduler中获取request,以便提交给Downloader下载页面;
    3. 之后Engine会向Scheduler进行请求一下个需要爬取的url;
    4. 如果Engine将url 发送给Downloader进行下载,这个过程会经过DownloaderMiddleware,此时会通过函数process_request()进行request处理
    5. Downloader在下载页面之后,生成response并返回给Engine,这个过程同样会经过DownloaderMiddleware,此时会通过process_response()进行response处       理,如果出现错误异常,则会经过process_exception()进行错误处理;
    6. Engine在从Downloader接收到Response返回给spider,整个过程会经过SpiderMiddleware,在处理过程中response通过process_spider_input()进行处理;
    7. Spider处理response时,会有两种情况,获取新的request给Engine,或者返回item到Engine,两种情况下都会从SpiderMiddleware下经过            		process_spider_output(),再出现错误异常的时候回经过process_spider_exception()进行处理;
    8. Engine将Spider返回的item给到Item Pipeline进行处理,如果是新的request,则继续返回给Scheduler;
    9. 重复直到Scheduler不再产生新的request,此时Engine关闭,程序执行结束。
12.常见的页面数据抽取方式有哪些?
    xpath
    beautifulsoup4
    re
    jsonpath
13.re.match与re.search的区别  findall与finditer的区别
答案:
    re.match:尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回None;
    re.search:扫描整个字符串并返回第一个成功的匹配
    match于search的区别:re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,知道找到一个匹     	 配;
    re.findall:在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表.注意:match和search是匹配一次findall匹配所有;
    re.finditer和findall类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回
14.scrapy分为哪几个组成部分?分别有什么作用?
答案:
	Spiders(爬虫类): Spiders是开发者自定义的一个类,用于解析相应并提取item或下个爬取的URL

	Scrapy Engine(引擎): Engine负责控制数据流在系统中的流动走向,并在指定条件下触发一些事件。同时,也可以简单理解为Scrapy中数据流的中转站

	Scheduler(调度器): Scheduler接收Engine发出的requests,并将这些requests放入到处理队列中,以便之后Engine需要时再提供

	Downloader(下载器): Downloader负责抓取网页信息并提供给Engine,进而转发至Spider

	Item Pipeline(处理管道): Item Pipeline负责处理Spiders类处理提取之后的item,典型的处理有数据清洗,验证以及持久化
15.爬虫常见的反反爬技巧有哪些?
	答:
		一:做好伪装。请求头、模拟浏览器登陆等等。
		二:请求频率降低。time.sleep(1),或者download.delay = 1  ,  对某些网站已经够用
		三:上代理。效果最好的方式,只是繁琐了很多,甚至需要购买好用的代理。
16.如果让你来防范网站爬虫,你应该怎么来提高爬取的难度 ?
答案:
	 1.爬取行为是对页面的源文件爬取,如爬取静态页面的html代码,可以用jquery去模仿写html
	 2.对Headers的User-Agent进行检测
	 3.通过检测用户行为,同一IP短时间内多次访问同一页面,或者同一账户短时间内多次进行相同操作
	 4.数据通过ajax来获取
17.是否搭建过爬虫集群,集群是如何实现节点发现和管理的 ?
答案:
    针对将海量爬虫节点组织成全分布式爬虫集群所遇到的高效、均衡、可靠、可拓展等问题,提出了一种基于Kademlia的全分布式爬虫集群方法。该方法通过改进的    		Kademlia技术建立起爬虫节点间的底层通信机制。在此基础上,根据Kademlia的异或特性及节点的可用资源情况,设计并实现具有任务划分、异常处理、节点加入退出处	  理及负载均衡的全分布式爬虫集群模型。在实际网络系统上的实验结果表明,该方法能有效利用海量弱计算终端的计算、存储和带宽资源,构建高效、均衡、可靠、可大规     模拓展的全分布式爬虫集群。
18.遇到过得反爬虫策略以及解决方法	
    1.通过headers反爬虫
    2.基于用户行为的发爬虫:(同一IP短时间内访问的频率)
    3.动态网页反爬虫(通过ajax请求数据,或者通过JavaScript生成)
    4.对部分数据进行加密处理的(数据是乱码)
    解决方法:
    对于基本网页的抓取可以自定义headers,添加headers的数据
    使用多个代理ip进行抓取或者设置抓取的频率降低一些,
    动态网页的可以使用selenium + phantomjs 进行抓取
    对部分数据进行加密的,可以使用selenium进行截图,使用python自带的pytesseract库进行识别,但是比较慢最直接的方法是找到加密的方法进行逆向推理。

【生长吧!Python】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/278897

(完)