http://www.javashuo.com/article/p-uqppchod-gd.htmljavascript
""" 插入cell: a b 删除: x 执行:shift+enter tab: cell模式切换:y(m->code) m(code=>m) shift+tab:打开官方文档 """ import numpy as np np.linspace() # shift+tab:打开官方文档
https://www.jianshu.com/p/836a79d1f8a4 # 安装anaconda
https://www.jianshu.com/p/87ed39c4e01b # 使用 https://zhuanlan.zhihu.com/p/34337292 # 常见问题 https://blog.csdn.net/Data_Arrow/article/details/89188256 # 修改文件目录,以及修改默认浏览器 https://blog.csdn.net/Data_Arrow/article/details/88381388 # 修改文件目录,以及修改默认浏览器
爬虫:经过编写程序,模拟浏览器上网,而后让其去互联网上爬取数据的过程 爬虫的分类: 通用爬虫: 聚焦爬虫: 增量式: 反爬机制: 反爬策略: robots.txt协议:听从或者不听从
抓包工具:fiddlerphp
1.指定url 2.发起请求 3.获取相应内容 4.保存数据 import requests # 1 url = "https://www.sogou.com" # 2 response = requests.get(url=url) # 3 page_text = response.text # 4 with open("sogou.html", "w", encoding="utf-8") as f: f.write(page_text)
import requests url = "https://www.sogou.com/web" # 封装参数 wd = input("enter a word:") params = { "query": wd } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } response = requests.get(url=url, params=params, headers=headers) # page_text = response.text # 字符串 page_text = response.content # 二进制 filename = wd + ".html" with open(filename, "wb") as f: f.write(page_text)
import requests url = "https://fanyi.baidu.com/sug" # 封装参数 wd = input("enter a word:") date = { "kw": wd } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } response = requests.post(url=url, data=date, headers=headers) page_text = response.text # 字符串 # page_text = response.content # 二进制 # page_text = response.json # 返回对象,若是没有结果会报错 print(response.json())
js请求优势难度,由于由于是异步的因此你须要本身去找数据刷新的接口,同时下一个页面一定使用了上一个页面中的参数,不然根本没办法联系起来,因此就须要你本身去找规律。 import requests url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } # 封装参数 # wd = input("enter a word:") id_list = [] for page in range(1, 2): date = { "on": "true", "page": "page", "pageSize": "15", "productName": "", "conditionType": "1", "applyname": "", "applysn": "", } response = requests.post(url=url, data=date, headers=headers) # page_text = response.text # 字符串 # page_text = response.content # 二进制 # page_text = response.json() # 对象,若是没有结果会报错 page_text = response.json() for dic in page_text["list"]: id = dic["ID"] id_list.append(id) detail_url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById" for id in id_list: date = { "id": str(id) } data = requests.post(url=detail_url, data=date, headers=headers).json() print(data)
import requests url = "https://up.ruyile.com/jy_img/11/234/151448ie4.jpg" response = requests.get(url=url, headers=headers) with open("校花.jpg", "wb") as f: f.write(response.content) # import urllib url = "https://up.ruyile.com/jy_img/11/234/151448ie4.jpg" urllib.request.urlretrieve(url=url, filename="123.jpg")
import re string = """fall in love with you i love you very mush i love she i love her""" print(re.findall("^i.*", string, re.M)) # 对字符串进行多行匹配 string1 = """<div>细思极恐, 你的队友在看书, 你的敌人在磨刀, 你的闺蜜在减肥, 隔壁老王在睡觉。</div>""" print(re.findall("^.*", string1, re.S)) # 将整个字符串做为一行
import re import requests # url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList" url = "https://www.neihanba.com/pic/" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } response = requests.get(url=url, headers=headers) # 其中<li class="piclist[23]">.*?</li>匹配li标签中的全部,以后再加上<img src="(.*?)" alt=.*?前面过滤出来的内容进行二次匹配src中的内容。 image_list = re.findall('<li class="piclist[23]">.*?<img src="(.*?)" alt=.*?</li>', response.text, re.S) print(image_list) for url in image_list: print(url) """ <li class="piclist2"> <a href="/pic/1175728.html" target="_blank" class="imgbox"> <img src="https://file.neihanba.com/file/2020/02/08/smalla37b627df679038d444105686bd6c4f4.jpg" alt="这妹子自带安全气囊" width="100" height="100"></a> <h4> <a target="_blank" href="/pic/1175728.html">这妹子自带安全气囊</a><span class="new">new</span></h4> <p class="g9"> </p> <p class="ft"> <span class="r">2020-02-08</span> <span class="view" title="浏览次数"><i></i><em>0</em></span> <span class="good" title="好评"><i></i><em>0</em></span> <span class="bad" title="差评"><i></i><em>0</em></span> </p> </li> """
http://www.javashuo.com/article/p-eupvvyhc-ed.html bs4以及xpath使用html
pip install bs4 pip install lxml 解析原理: 1.即将要进行解析的原码加载到bs对象 2.调用bs对象中的相关方法或属性进行源码中的相关标签的定位 3.将定位到的标签之间存在的文本或者属性值获取 - 须要将pip源设置为国内源,阿里源、豆瓣源、网易源等 - windows (1)打开文件资源管理器(文件夹地址栏中) (2)地址栏上面输入 %appdata% (3)在这里面新建一个文件夹 pip (4)在pip文件夹里面新建一个文件叫作 pip.ini ,内容写以下便可 [global] timeout = 6000 index-url = https://mirrors.aliyun.com/pypi/simple/ trusted-host = mirrors.aliyun.com - linux (1)cd ~ (2)mkdir ~/.pip (3)vi ~/.pip/pip.conf (4)编辑内容,和windows如出一辙 - 须要安装:pip install bs4 bs4在使用时候须要一个第三方库,把这个库也安装一下 pip install lxml 基础使用 ---------------------------------------------------------------------------------------- 使用流程: - 导包:from bs4 import BeautifulSoup - 使用方式:能够将一个html文档,转化为BeautifulSoup对象,而后经过对象的方法或者属性去查找指定的节点内容 (1)转化本地文件: - soup = BeautifulSoup(open('本地文件'), 'lxml') (2)转化网络文件: - soup = BeautifulSoup('字符串类型或者字节类型', 'lxml') (3)打印soup对象显示内容为html文件中的内容 基础巩固: (1)根据标签名查找 - soup.a 只能找到第一个符合要求的标签 (2)获取属性 - soup.a.attrs 获取a全部的属性和属性值,返回一个字典 - soup.a.attrs['href'] 获取href属性 - soup.a['href'] 也可简写为这种形式 (3)获取内容 - soup.a.string # 这个是获取单个内容 - soup.a.text # 获取标签中的全部内容,嵌套一样获取,返回列表 - soup.a.get_text() # 获取标签中的全部内容,嵌套一样获取,返回列表 【注意】若是标签还有标签,那么string获取到的结果为None,而其它两个,能够获取文本内容 (4)find:找到第一个符合要求的标签 - soup.find('a') 找到第一个符合要求的 - soup.find('a', title="xxx") - soup.find('a', alt="xxx") - soup.find('a', class_="xxx") - soup.find('a', id="xxx") (5)find_all:找到全部符合要求的标签 - soup.find_all('a') - soup.find_all(['a','b']) 找到全部的a和b标签 - soup.find_all('a', limit=2) 限制前两个 (6)根据选择器选择指定的内容 select:soup.select('#feng') - 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器 - 层级选择器: div .dudu #lala .meme .xixi 下面好多级 div > p > a > .lala 只能是下面一级 【注意】select选择器返回永远是列表,须要经过下标提取指定的对象
bs4使用:java
from bs4 import BeautifulSoup import requests url = "http://www.shicimingju.com/book/sanguoyanyi.html" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } response = requests.get(url=url, headers=headers) page_text = response.text soup = BeautifulSoup(page_text, "lxml") a_list = soup.select(".book-mulu > ul > li > a") # 返回的是a标签 f = open("sangu.txt", "w", encoding="utf-8") for a in a_list: title = a.string detail_url = "http://www.shicimingju.com" + a["href"] page_text2 = requests.get(url=detail_url, headers=headers).text soup2 = BeautifulSoup(page_text2, "lxml") content = soup2.find("div", class_="chapter_content").text f.write(title + "\n" + content) print(title, "over") print("over") f.close()
环境安装:pip install lxml 解析原理: 获取页面源码数据 实例化一个etree的对象,而且加你页面源码加载到该对象中 调用到该对象的xpath方法进行指定标签的定位 注意:xpath函数必须结合着xpath表达式进行标签订位和内容捕获 -------------------------------------------------------------------------------- # 经常使用xpath表达式回顾 属性定位: #找到class属性值为song的div标签 //div[@class="song"] 层级&索引定位: #找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a //div[@class="tang"]/ul/li[2]/a 逻辑运算: #找到href属性值为空且class属性值为du的a标签 //a[@href="" and/or @class="du"] 模糊匹配: //div[contains(@class, "ng")] //div[starts-with(@class, "ta")] 取文本: # /表示获取某个标签下的文本内容 # //表示获取某个标签下的文本内容和全部子标签下的文本内容 //div[@class="song"]/p[1]/text() //div[@class="tang"]//text() 取属性: //div[@class="tang"]//li[2]/a/@href 代码中使用xpath表达式进行数据解析: 1.下载:pip install lxml 2.导包:from lxml import etree
import requests from lxml import etree url = "https://bj.58.com/ershoufang/" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } response = requests.get(url=url, headers=headers) page_text = response.text tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@class="house-list-wrap"]/li') for li in li_list: text = li.xpath('./div[2]/h2/a/text()')[0] print(text) price = li.xpath('./div[3]//text()') print("".join(price))
案例一:中文乱码node
# 解决爬虫中文问题 # 1 对整个返回的结果进行从新的编码 response = requests.get(url=url, headers=headers) response.encoding = 'utf-8' page_text = response.text # 上面有时候不能解决编码的问题,使用局部解决办法 # 2 对须要的文字进行从新编码 title = li.xpath('./a/h3/text()')[0].encode('iso-8859-1').decode('gbk') # 3 所有从新编码 response = requests.get(url=url).text.encode('iso-8859-1').decode('utf-8') import requests from lxml import etree url = 'https://www.xxx.com.cn' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } page_text = requests.get(url=url, headers=headers).text tree = etree.HTML(page_text) li_list = tree.xpath('//div[@id="auto-channel-lazyload-article"]/ul/li') for li in li_list: try: title = li.xpath('./a/h3/text()')[0].encode('iso-8859-1').decode('gbk') a_url = f"https:{li.xpath('./a/@href')[0]}" img_src = f"https:{li.xpath('./a/div/img/@src')[0]}" desc = li.xpath('./a/p/text()')[0].encode('iso-8859-1').decode('gbk') except IndexError: continue print(title) print(a_url) print(img_src) print(desc)
案例二:中文乱码python
import os import urllib import requests from lxml import etree url = 'http://pic.netbian.com/4kmeinv' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } response = requests.get(url=url, headers=headers) # 1.方式一 # response.encoding = "utf-8" page_text = response.text tree = etree.HTML(page_text) li_list = tree.xpath("//div[@class='slist']/ul/li") if not os.path.exists("meinv"): os.mkdir("meinv") for li in li_list: img_name = li.xpath("./a/b/text()")[0] # 2.方式二 img_name = img_name.encode("ISO-8859-1").decode("gbk") img_url = "http://pic.netbian.com" + li.xpath("./a/img/@src")[0] filename = "./meinv/" + img_name + ".jpg" urllib.request.urlretrieve(url=img_url, filename=filename) print("ok", img_url) print("over")
案例三:加密图片mysql
import base64 import requests from lxml import etree url = 'http://jiandan.net/ooxx' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } response = requests.get(url=url, headers=headers) # 1.方式一 # response.encoding = "utf-8" page_text = response.text tree = etree.HTML(page_text) span_list = tree.xpath('//span[@class="img-hash"]/text()') for im in span_list: # 这个网站之前的图片的是通过bs64加密的因此须要使用 img = "http://" + base64.b64decode(im).decode() print(img)
案例四:简历爬取linux
import os import requests from lxml import etree import urllib import random url = "http://sc.chinaz.com/jianli" headers = { # "Connection": "close", # 防止由于请求过快,而致使ip被封,相应以后直接断开 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } if not os.path.exists("jianli"): os.mkdir("jianli") response = requests.get(url=url, headers=headers) response.encoding = "utf-8" page_text = response.text tree = etree.HTML(page_text) li_list = tree.xpath('//div[@id="container"]/div') for li in li_list: print("1") detail_url = li.xpath("./a/@href")[0] name = li.xpath("./a/img/@alt")[0] detail_page = requests.get(url=detail_url, headers=headers).text print("2") tree2 = etree.HTML(detail_page) down_list = tree2.xpath("//div[@class='clearfix mt20 downlist']/ul/li/a/@href") down_url = random.choice(down_list) print("3") down_page = requests.get(url=down_url, headers=headers).content # 图片,文件,使用content直接拿取内容 print("4") file_path = "jianli/" + name + ".rar" with open(file_path, "wb") as f: f.write(down_page) print(name, "下载完成") print("over")
案例五:城市信息web
url = "https://www.aqistudy.cn/"
当某些资源出如今屏幕的某些高度的时候,才加载资源,能够加少服务器压力,优化页面,提升用户体验。
www.goubanjia.com 快代理 西刺代理
# 普通用户和开发者用户都要注册 - 登陆普通用户(查看余额) - 登陆开发者用户 - 建立个人软件 - 下载云打码的python3http事例代码 import requests from lxml import etree import urllib session = requests.Session() # 获取请求生成的session headers = { # "Connection": "close", # 防止由于请求过快,而致使ip被封,相应以后直接断开 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } url = "http://www.renren.com" page_text = session.get(url=url, headers=headers) tree = etree.HTML(page_text) conde_img_url = tree.xpath("//*[id='verifyPic_login']/@src") # 这里获取到图片以后须要使用云打码平台提取验证码并返回 urllib.request.urlretrieve(url=conde_img_url, filename="code.jpg") # 登陆 url = "http://www.renren.com/289676607/profile" # data中存放的就是用户fillder抓取的登陆时的全部参数,将对应的参数进行补充就能够发post请求登陆了 data = {} session.post(url=url, data=data, headers=headers) # 获取登录以后页面的数据,这里的session中已经将cookie的值自动保存了 page_text = session.get(url=url, headers=headers).text
- pip install selenium - 编码流程 - 导报:from selenium import webdirver - 实例化某一个浏览器对象 - 自制定自动化操做 import time from selenium import webdirver # 这里面须要去下载谷歌的驱动,其中的path就是你下载的谷歌驱动存放路径 # http://chromedriver.storage.googleapis.com/index.html下载地址 brower = webdirver.Chorme(executable_path=path) text_input = brower.find_element_by_id("kw") text_input.send_keys("人民币") brower.find_element_by_id("su").click() time.sleep(2) # 获取页面源码数据 page_text = brower.page_source brower.quit()
import time # 无头设置 # 方式一 from selenium.webdriver.chrome.options import Options chrom_options = Options() chrom_options.add_argument("--headless") chrom_options.add_argument("--disable-gpu") brower= Chrome(chrom_options=chrom_options) # 方式二:使用PhantomJS,可是他已经中止更新了,直接使用PhantomJS的.exe文件替换executable_path中的使用的谷歌驱动就好了 # 规避检测 from selenium.webdriver import ChromeOptions option = ChromeOptions() option.add_experimental_option('excludeSwitches', ['enable-automation']) driver = Chrome(options=option) from selenium import webdriver url = "https://movie.douban.com/explore#!type=movie&tag=%E5%86%B7%E9%97%A8%E4%BD%B3%E7%89%87&sort=recommend&page_limit=20&page_start=0" brower = webdriver.Chrome(executable_path=r"C:\Users\lzh\Downloads\chromedriver.exe") brower.get(url) time.sleep(3) brower.execute_script("window.scrollTo(0, document.body.scrollHeight)") time.sleep(3) brower.execute_script("window.scrollTo(0, document.body.scrollHeight)") time.sleep(3) brower.execute_script("window.scrollTo(0, document.body.scrollHeight)") page_text = brower.page_source with open("./douban.html", "w", encoding="utf-8") as fp: fp.write(page_text) time.sleep(1) brower.quit()
iframe标签:ajax
import time from selenium import webdriver url = "https://qzone.qq.com/" brower = webdriver.Chrome(executable_path=r"C:\Users\lzh\Downloads\chromedriver.exe") brower.get(url) # 切换到iframe标签中 brower.switch_to.frame("login_frame") click_btn = brower.find_element_by_id("switcher_plogin").click() text_input = brower.find_element_by_id("u") text_input.send_keys("3164626382") text_input = brower.find_element_by_id("p") text_input.send_keys("lzh19950326") click_but = brower.find_element_by_id("login_button").click() time.sleep(2) # 获取页面源码数据 page_text = brower.page_source # 可使用tree对页面源码进行解析 brower.quit()
import requests import re from lxml import etree from multiprocessing.dummy import Pool import random pool = Pool(5) url = "https://www.pearvideo.com/category_1" headers = { # "Connection": "close", # 防止由于请求过快,而致使ip被封,相应以后直接断开 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" } response = requests.get(url, headers).text tree = etree.HTML(response) li_list = tree.xpath("//div[@id='listvideoList']/ul/li") video_url_list = [] for li in li_list: detail_url = "https://www.pearvideo.com/" + li.xpath("./div/a/@href")[0] detail_text = requests.get(url=detail_url, headers=headers).text video_url = re.findall('srcUrl="(.*?)",vdoUrl', detail_text, re.S)[0] video_url_list.append(video_url) def getVideoData(url): content = requests.get(url, headers=headers).content return content view_data_list = pool.map(getVideoData, video_url_list) def seveVideoData(data): filename = str(random.randint(0, 100)) + ".mp4" with open(filename, "wb") as f: f.write(data) pool.map(seveVideoData, view_data_list)
# 回顾 - 开发者用户: - 建立一个软件 - 下载示例代码 - 超时时间 - 模拟登录: - 爬取一些基于当前用户的相关数据信息 - 验证码 - cookie - 动态数据加载 - selenium phantomjs 谷歌无头浏览器 驱动程序 - 做用:爬取一些动态加载的页面数据 # 移动端数据加载 - 配置fiddler - tools -> options -> connection -> allow remote computer to connect - fiddler prot : xxxx - 移动端安装fiddler的证书 - 保证移动端和fiddler所在的pc的网络是在同一个网段下 - 在移动端浏览器中:fiddler所在机器的ip地址:fiddler的端口号 - 证书下载完毕以后进行安装且设置信任 - 配置手机的网络 - 给手机设置一个代理ip:port
# 分析 # 1改变页面中的查询条件,而后点击插叙按钮,经过抓包工具捕获相关的数据包,最终定位到数据包的请求url # 2该数据包中发现:post请求携带了一个动态变化且加密的请求参数,d,而且请求到的数据也是一组密文数据 # 3发现点击了查询按钮以后发送了一个ajax请求,该请求将请求参数加密以后发起了post请求 # 4经过火狐定位到搜素按钮绑定的click点击事件(getData) # 5剖析getData函数的实现: # type=HOUR # getAOIData() ,getWeatherData() #6.# getAOIData() ,getWeatherData()中的内容基本一致 # getServerData # param的字典:有4个键值 # 调用了getServerData(method, param, 匿名函数,0.5) #7.剖析getServerdata # 在谷歌抓包工具中,定位到etServerdata的实现,发现函数被加密了,js混淆 # js反混淆 # 解密网址:http://www.bm8.com.cn/jsConfusion/ #8 getServer反混淆以后的代码 # 发现getParam函数,发现加密以后的请求参数 # decodeData(data):时间加密的响应数据进行解密的 # 10 须要经过python调用js的相关代码:PyexecJs # 可让python对js代码进行模拟运行 # pip install PyExecJS # 安装node.js环境 # 11将反混淆以后的呢一段js代码放入一个js文件中 # d懂事编写以下的js函数拿到加密以后的请求参数 # function getPostParamCode(method, city, type, startTime, endTime){ # var param = {}; # param.city = city; # param.type = type; # param.startTime = startTime; # param.endTime = endTime; # return getParam(method, param); # } # 12接下来就是使用 # 经过 PyExecJS编译那一个js文件,以后使用execj经过python调用js import requests import execjs node = execjs.get() # Params method = 'GETCITYWEATHER' city = '北京' type = 'HOUR' start_time = '2018-01-25 00:00:00' end_time = '2018-01-25 23:00:00' # Compile javascript file = 'jsCode.js' ctx = node.compile(open(file).read()) # Get params js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time) params = ctx.eval(js) # 发起post请求 url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php' response_text = requests.post(url, data={'d': params}).text # 对加密的响应数据进行解密 js = 'decodeData("{0}")'.format(response_text) decrypted_data = ctx.eval(js) print(decrypted_data) """ 动态变化的请求参数 js加密 js混淆 """
https://www.bilibili.com/video/av84457418?p=602
# 安装 # Linux: pip3 install scrapy # Windows: a. pip3 install wheel b. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/ #twisted c. 进入下载目录,执行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl d. pip3 install pywin32 e. pip3 install scrapy # Scrapy异步框架 -pySpider # 什么是框架? - 就是一个就有很强通用性且集成了不少功能的项目模板(能够被引用在各类需求中) # scrapy集成好的功能 - 高性能的数据解析操做(xpath) - 高性能的数据下载 - 高新能的持久化存储 - 中间件 - 全栈数据爬取数据 - 分布式,redis - 请求传参的机制(深度爬取) - scrapy中合理的应用selenium # 建立工程 - scrapy startproject ProName - cd ProName - scrapy genspider spiderName www.xxx.com : 建立爬虫文件 - scrapy crawl sipderName # 执行 - settings.py文件中对下面两个参数进行配置 - USER_AGENT = "" - ROBOTSTXT_OBEY = False # 若是不设置日志等级,scrapy crawl sipderName会输出一大堆日志,可是其中不少日志信息是没用 - LOG_LEVLE = 'ERROR' 指定日志登记 - LOG_FILE = "./data/log.log" # 指定log文件的文件目录 # scrapy数据解析 -extract() : 列表是有多个元素 -extract_first(): 单个元素 # scrapy持久化存储 - 基于终端指令: - 只能够将parse方法中的返回值存储到磁盘文件中 - scrapy crawl first -o fileName.csv # 后缀必须是.csv.json.lxml --------------------------------------基于终端指令存储文件代码--------------------------------------- import scrapy class FirstSpider(scrapy.Spider): # 爬虫文件名名称,爬虫源文件的惟一标识 name = 'first' # 容许的域名:下载url列表中的域名 # allowed_domains = ['www.xxx.com'] # 其实的url列表:列表中的立标元素会被scrapy自动的进行请求发送 start_urls = ['https://dig.chouti.com/'] # 解析数据 def parse(self, response): data_list =[] div_list = response.xpath("/html/body/main/div/div/div[1]/div/div[2]/div[1]") for div in div_list: # 注意:xpath返回的列表中的元素是Selector对象,咱们要解析获取到的字符串数据是存储在该对象中的 # 必须进过一个extract()的操做才能够将该对象中存储的字符串数据获取 # .extract()返回的是一个列表 .extract_first()返回的是单个值 # content = div.xpath("./div/div/div[1]/a/text()")[0].extract() content = div.xpath("./div/div/div[1]/a/text()").extract_first() # xpath返回的列表中的列表元素有多个(selector对象),想要将每个列表元素多对应的Selector中的 # 字符串提取出来该如何操做?response.xpath(/div//text()).extract() print(content) # selector xxx="fasdf" data="fasdfds" data_list.append(content) return data_list -------------------------------------------结束------------------------------------------- - 基于管道:pipelines.py - 编码流程 - 1.数据解析 - 2.在item的类中定义相关的属性名称,item.py中 import scrapy class FirstBloodItem(scrapy.Item): content = scrapy.Field() # filed是一个万能的数据类型 - 3.将解析的数据存储封装到item类型的对象中,item["content"], spiderName.py中 content = div.xpath("./div/div/div[1]/a/text()").extract_first() item = FirstBloodItem() item["content"] = content - 4.将item兑现提交给管道 ,spiderName.py中 yield item - 5.在管道类中process_item负责接收item对象,对item数据进行任意持久化存储(piplines.py) # 专门用做于持久化存储 class FirstBloodPipeline(object): fp = None def open_spider(self, spider): print("只会在爬虫开始的时候执行一次") self.fp = open("./data.txt", "w", encoding="utf-8") def process_item(self, item, spider): content = item["content"] self.fp.write(content) return item def close_spider(self, spider): print("只会在爬虫结束的时候执行一次") self.fp.close() - 6.在配置文件settings.py中开启管道:ITEM_PIPELINES参数 ITEM_PIPELINES = { 'first_blood.pipelines.FirstBloodPipeline': 300, # 300表示的是优先级,这个值越小,优先级越高 } - 补充: - 管道文件中的一个管道类表示将数据存储到某一种形式的平台 - 若是管道文件中定义了多个管道类,爬虫类提交的item会给到优先级最高的管道类 - process_item方法的实现中的return item的操做表示将item传递给下一个即将被执行的管道类 ----------------------------------------基于管道----------------------------------- # 管道的持久化存储 def parse(self, response): div_list = response.xpath("/html/body/main/div/div/div[1]/div/div[2]/div[1]/div") for div in div_list: # 注意:xpath返回的列表中的元素是Selector对象,咱们要解析获取到的字符串数据是存储在该对象中的 # 必须进过一个extract()的操做才能够将该对象中存储的字符串数据获取 # .extract()返回的是一个列表 .extract_first()返回的是单个值 # content = div.xpath("./div/div/div[1]/a/text()")[0].extract() content = div.xpath("./div/div/div/div[1]/a/text()").extract_first() item = FirstBloodItem() item["content"] = content # xpath返回的列表中的列表元素有多个(selector对象),想要将每个列表元素多对应的Selector中的 # 字符串提取出来该如何操做?response.xpath(/div//text()).extract() print(content, "*" * 50) # selector xxx="fasdf" data="fasdfds" yield item # 经过yield关键字将数据传输到pipline中 -------------------------------------------------------------------------
settings.py ITEM_PIPELINES = { # 'huyapro.pipelines.HuyaproPipeline': 300, # 'huyapro.pipelines.mysqlPipeLine': 301, 'huyapro.pipelines.redisPipeLine': 302, } piplines.py import pymysql from redis import Redis class HuyaproPipeline(object): fp = None def open_spider(self, spider): self.fp = open("huya.txt", "w", encoding="utf-8") print("open file") def process_item(self, item, spider): # item就是接收到的爬虫类提交过来的item对象 self.fp.write(item["title"] + ":" + item["author"] + ":" + item["orders"] + "\n") print("写入成功") return item def close_spider(self, spider): self.fp.close() print("close file") class mysqlPipeLine(object): def open_spider(self, spider): self.conn = pymysql.Connection( host="127.0.0.1", user="root", password="1", database="spider", port=3306) print(self.conn) def process_item(self, item, spider): sql = "insert into huya values('{}','{}','{}')".format(item["title"], item["author"], item["orders"]) self.cursor = self.conn.cursor() try: self.cursor.execute(sql) self.conn.commit() except Exception as e: print(e) def close_spider(self, spider): self.cursor.close() self.conn.close() print("close") class redisPipeLine(object): conn = None def open_spider(self, spider): self.conn = Redis(host="192.168.31.199", port=6379) print(self.conn) def process_item(self, item, spider): self.conn.lpush("huyaList", item) return item
# 管道的持久化存储 - 数据解析(爬虫类) - 讲解洗出来的数据封装到item类的对象中(爬虫类) - 将爬取到的数据提价给管道(爬虫类) - 在管道类中的process_item中接收item对象并进行任意形式的持久化存储(管道类) - 在配置文件中开启管道类 - 细节: - 将爬取到的数据进行备份? - 一个管道类对应一个平台的持久化存储 - 有多个管道类是否意味着多个管道类均可以接受到爬虫文件提交的item? - 只有优先级最高的管道才能够接收到item,剩下管道类是须要从优先级最高的管道类接收参数 # 基于Spider父类进行全栈数据爬取: - 全站数据的爬取:将全部页码对应的页面上数据进行解析 - 手动请求的发送: - yield scrapy.Request(url, callback) - 对yield总结: - 向管道提交item的时候:yeild item - 手动请求发送:yield scrapy.Request(url, callback) - 手动发送post请求: yield scrapy.FormRequest(url, formdata, callback) # formdata是一个字典形式的请求参数 --------------------------------------------全栈数据爬取-------------------------------------------- import scrapy from huyaall.items import HuyaallItem class HuyaSpider(scrapy.Spider): name = 'huya' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.huya.com/g/xingxiu'] # 通用的url模板 url = "https://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&gameId=1663&tagAll=0&page=%d" def parse(self, response): li_list = response.xpath('//*[@id="js-live-list"]/li') for li in li_list: title = li.xpath("./a[2]/text()").extract_first() author = li.xpath("./span/span[1]/i/text()").extract_first() orders = li.xpath("./span/span[2]/i[2]/text()").extract_first() # 实例化item类类型的对象 item = HuyaallItem() item["title"] = title item["author"] = author item["orders"] = orders yield item # 手动请求的发送 for i in (2, 5): new_url = format(self.url % i) # 发起的是get请求 yield scrapy.Request(url=new_url, callback=self.parse_other) # 全部的解析方法必须根据parse进行定义,必需要有parse一样的参数 def parse_other(self, response): print(response.text)
# scrapy五大核心组件: 上图就是scrapy五大核心组件 1.spider获取到url传输给引擎 2.引擎将url传输给调度器 3.调度器经过过滤器对重复的url进行过滤,并将过滤好的url放入队列中 4.引擎从队列中拿去url返回给下载器, 5.下载器到互联网上请求数据 6.返回数据给下载器response 7.下载将response交给引擎,引擎再将数据交给spider 8.spider拿到数据以后对数据进行解析xpath等 9.spider将数据交给引擎,引擎将数据传输给管道
#scrapy的请求参数: - 做用: 实现深度爬取 - 使用场景: 爬去的数据不在同一个页面中 - 传递item: yield scrapy.Request(detail_url, callback=self.parse_detail, meta={"item": item}) - 接收item: respone.meta
http://www.javashuo.com/article/p-tpzlcfow-cv.html
# 提高scrapy爬取数据的效率 - 在配置文件中进行相关设置 # 增长并发: 默认scrapy开启的并发线程为32个,能够适当进行增长。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。 # 下降日志级别: 在运行scrapy时,会有大量日志信息的输出,为了减小CPU的使用率。能够设置log输出信息为INFO或者ERROR便可。在配置文件中编写:LOG_LEVEL = ‘INFO’ # 禁止cookie: 若是不是真的须要cookie,则在scrapy爬取数据时能够进制cookie从而减小CPU的使用率,提高爬取效率。在配置文件中编写:COOKIES_ENABLED = False # 禁止重试: 对失败的HTTP进行从新请求(重试)会减慢爬取速度,所以能够禁止重试。在配置文件中编写:RETRY_ENABLED = False # 减小下载超时: 若是对一个很是慢的连接进行爬取,减小下载超时能够能让卡住的连接快速被放弃,从而提高效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s
# scrapy的中间件: - 爬虫中间件 - 下载中间件(重点):处于引擎和下载器之间 - 做用:批量拦截全部的请求和响应 - 问什么拦截请求 - 篡改请求的头信息(UA假装) - 修改请求对应的ip(代理) - 问什么拦截响应 - 篡改响应数据, 篡改响应对象
- 爬取网易新闻的新闻标题和内容 - selenium在scrapy中的使用流程 - 在爬虫类中定义一个bro的属性,就是实例化的浏览器对象 - 在爬虫类中重写父类的一个closed(self,spider),在方法中关闭bro - 在中间中进行浏览器自动化的操做 # 做业 - 网易新闻 - http://sc.chinaz.com/tupian/xingganmeinvtupian.html
# midddlerwares.py import time from scrapy.http import HtmlResponse class WangyiproDownloaderMiddleware(object): def process_response(self, request, response, spider): """ 参数: :param request: 拦截到的请求对应的响应对象 :param response: 拦截到全部的响应对象(1+5+n) :param spider: 爬虫类实例化的对象,能够实现爬虫类和中间类的数据交互 :return: # 拦截五个板块的响应对象,将其替换成五个符合需求的新的相应对象进行返回 """ if request.url in spider.model_list: # url:相应对象的请求对象的url # body:响应数据, 可使用selenium请求拿出page_source bro = spider.bro bro.get(request.url) time.sleep(3) page_text = bro.page_source new_response = HtmlResponse(url=request.url, body=page_text, encoding="utf-8", request=request) return new_response else: return response ------------------------------------------------------------------------- # piplines.py import pymysql class WangyiproPipeline(object): conn = None cursor = None def open_spider(self, spider): self.conn = pymysql.Connection(host="127.0.0.1", user="root", password="1", database="spider", port=3306) print(self.conn) def process_item(self, item, spider): print("进入process_item函数") sql = "insert into wangyi values ('{}', '{}')".format(item["title"], item["content"]) print(sql) self.cursor = self.conn.cursor() try: self.cursor.execute(sql) self.conn.commit() except Exception as e: print(e) self.conn.rollback() return item def close_spider(self, spider): self.cursor.close() self.conn.close() ----------------------------------------------------------------------- # item.py import scrapy class WangyiproItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field() content = scrapy.Field() ----------------------------------------------------------------------- # spider/wangyi.py import scrapy from selenium import webdriver from wangyipro.items import WangyiproItem class WangyiSpider(scrapy.Spider): name = 'wangyi' # allowed_domains = ['www.xxx.com'] start_urls = ['https://news.163.com/'] model_list = [] bro = webdriver.Chrome(executable_path=r"C:\Users\lzh\Downloads\chromedriver.exe") def parse(self, response): # 解析五个板块对应的url li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li') model_index = [3, 4, 6, 7, 8] for index in model_index: li = li_list[index] # 5个板块对应的url modul_url = li.xpath('./a/@href').extract_first() self.model_list.append(modul_url) # 对每个模块对应的url发送请求 yield scrapy.Request(modul_url, callback=self.parse_module) def parse_module(self, response): # 该方法中获取到的response对象是没有包含动态加载出的新闻数据(是一个不知足需求的response) div_list = response.xpath("/html/body/div/div[3]/div[4]/div[1]/div/div/ul/li/div/div") for div in div_list: detail_url = div.xpath("./a/@href").extract_first() title = div.xpath("./div/div[1]/h3/a/text()").extract_first() item = WangyiproItem() item["title"] = title yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={"item": item}) def parse_detail(self, response): item = response.meta["item"] content = response.xpath('//*[@id="endText"]//text()').extract() content = "".join(content) item["content"] = content yield item def closed(self, spider): self.bro.quit()
- 图片懒加载 - 优化用户体验,应用到标签的伪属性,数据捕获的时候必定是基于伪属性进行的 - ImagePileline:专门是做用于数据下载和持久化存储的管道类 # piplinex.py import scrapy from scrapy.pipelines.images import ImagesPipeline class ImgproPipeline(ImagesPipeline): # 该方法是用来对媒体资源进行请求的(数据下载),参数item就是接收到的爬虫类提交的item对象 def get_media_requests(self, item, info): yield scrapy.Request(item["img_src"]) # 指明数据的存储路径(其实这里只是每一个文件的名称,存储文件须要在settings中设置) def file_path(self, request, response=None, info=None): return request.url.split("/")[-1] # 将item提交给下一个将被执行的管道类 def item_completed(self, results, item, info): print(results) # 这result很关键,能够打印看一下 return item ------------------------------------------------------------- # spider/img.py import scrapy from imgpro.items import ImgproItem class ImgSpider(scrapy.Spider): name = 'img' # allowed_domains = ['www.xx.com'] start_urls = ['http://sc.chinaz.com/tupian/xingganmeinvtupian.html'] def parse(self, response): div_list = response.xpath('//*[@id="container"]/div') for div in div_list: img_src = div.xpath('./div/a/img/@src2').extract_first() # item = ImgproItem() item = ImgproItem() img_src = img_src.replace("_s", "") item["img_src"] = img_src yield item ---------------------------------settings.py中------------------------------ # 图片存储文件夹的路径 IMAGES_STORE = "./imgLibs" # "生成图片缩略图,添加设置" IMAGES_THUMBS = { 'big': (600, 600), } # 下载图片大小的 # IMAGES_MIN_WIDTH = 110 # IMAGES_MIN_HEIGHT = 110 # 90天的图片失效期限 IMAGES_EXPIRES = 90 # 启用AutoThrottle扩展 AUTOTHROTTLE_ENABLED = True # 初始下载延迟(单位:秒) AUTOTHROTTLE_START_DELAY = 5 # 在高延迟状况下最大的下载延迟(单位秒) AUTOTHROTTLE_MAX_DELAY = 60 # 设置 Scrapy应该与远程网站并行发送的平均请求数, 目前是以1个并发请求数 AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0 # 启用AutoThrottle调试模式 #AUTOTHROTTLE_DEBUG = False
- 一种基于scrapy进行全站数据爬取的一种新的技术手段 - CrawlSpider就是一个Spider的子类 - 链接提取器:LinkExtractor - 规则解析器:Rule - 使用流程 - 新建一个工程 - cd 工程中 - 新建一个爬虫文件:scrapy genspider -t crawl SpiderName www.xxx.com
# spider/sun.py import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from sunpro.items import SunproItem, SunproDetailItem class SunSpider(CrawlSpider): name = 'sun' # allowed_domains = ['www.xxx.com'] start_urls = ['http://wz.sun0769.com/political/index/supervise?page=1'] # 实例化一个链接提取器 # 做用:根据制定规则(allow="正则")进行制定链接的提取 # link = LinkExtractor(allow=r'page=\d+') link = LinkExtractor(allow=r'supervise\?page=\d+') # 获取详情页链接 link_detail = LinkExtractor(allow=r"political/politics/index\?id=\d+") rules = ( # 将link做用到了Rule构造方法的参数1中 # 做用:将链接提取器取到的链接进行请求发送且根据指定的规则对请求到的数据进行解析 Rule(link, callback='parse_item', follow=False), # follow =True : 将链接提取器继续做用到,链接提取器提取到的链接所对应的页面 Rule(link_detail, callback='parse_detail', follow=False), ) def parse_item(self, response): # xpath表达式中是不能够出下tbody li_list = response.xpath('/html/body/div[2]/div[3]/ul/li') for li in li_list: title = li.xpath('./span[3]/a/text()').extract_first() num = li.xpath('./span[1]/text()').extract_first() item = SunproItem() item["title"] = title item["num"] = num yield item def parse_detail(self, response): num = response.xpath('/html/body/div[3]/div[2]/div[2]/div[1]/span[4]/text()').extract_first() num = num.split(":")[-1] content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first() item = SunproDetailItem() item["content"] = content item["num"] = num yield item ------------------------------------------------------------------------------ # pipline.py class SunproPipeline(object): def process_item(self, item, spider): if item.__class__.__name__ == "SunproItem": title = item["title"] num = item["num"] print(item) else: content = item["content"] num = item["num"] print(item) return item ------------------------------------------------------------------------------ # items.py import scrapy class SunproItem(scrapy.Item): title = scrapy.Field() num = scrapy.Field() class SunproDetailItem(scrapy.Item): content = scrapy.Field() num = scrapy.Field()
- 概念:须要搭建一个分布式的机群,而后再机群的每个电脑中执行同一组程序,让其对某一个网站的数据进行联合分布爬取 - 原生的scrapy框架是不能够实现分布式? - 由于调度器是不能够被共享 - 管道不能够被共享 - 如何实现分布式? - scrapy +scrapy-redis 实现分布式 - scrapy-redis组件的做用是什么 - 能够提供被共享的调度器和管道 - 特性:数据只能够存储在redis数据库 - 分布式的实现流程: - 1.pip install scrapy-redis - 2.建立工程 - 3.cd 工程目录 - 4.建立爬虫文件(a.建立基于spider的爬虫文件, b.建立基于crawlSpider的爬虫文件) - 5.修改爬虫类 - 导包:from scrapy_redis.spiders import RedisCrawlSpider - 修改继承类:class FbsSpider(RedisCrawlSpider): - 注释:allowed_domains , start_urls 注释掉 - 添加一个属性:redis_key = "fbsQueue" # 表示是能够被共享的调度器队列名称 - 编写爬虫文件:(常规操做) - 6. settings配置文件中配置 - UA假装 - robots - 管道的指定: ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400,} - 指定调度器: # 使用scrapy-redis组件的去重队列 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis组件本身的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置调度器是否要持久化,也就是当爬虫结束了,要不要清空Redis中请求队列和去重指定的set。若是是True表示要持久化,就不清空数据,不然清空数据 SCHEDULER_PERSIST = True - 指定redis数据库: REDIS_HOST = 'redis服务的ip地址' REDIS_PORT = 6379 REDIS_ENCODING = ‘utf-8’ REDIS_PARAMS = {‘password’:’123456’} - redis的配置文件进行配置redis.window.conf(随便赋值一个redis配置文件重命名) - 关闭默认绑定: 56line:# bing 127.0.0.1 - 关闭保护模式: 75line:protected-mode yes # 默认只能读不能写(须要修改no) - 启动redis的客户端以及服务端 redis-server redis.window.conf redis-cli - 启动程序: cd fbspro cd spider # 进入爬虫文件的目录中 scrapy runspider fbs.py - 向调度器的队列中扔入起始url: - 队列是存在与redis中 - 开启redis的客户端: lpush fbsQueue start_url # 这个时候可能会连不上redis,修改bind 0.0.0.0 ,同时protected-mode no,接着就是须要关闭防火墙,systemctl stop firewalld; setenforce 0
ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 } # 使用scrapy-redis组件的去重队列 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis组件本身的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置调度器是否要持久化,也就是当爬虫结束了,要不要清空Redis中请求队列和去重指定的set。若是是True表示要持久化,就不清空数据,不然清空数据 SCHEDULER_PERSIST = True REDIS_HOST = '192.168.31.199' REDIS_PORT = 6379 # 分布式爬虫:注意地方有两点,其中爬虫文件中的redis_key须要一致,同时settings中的redis_host须要需改成统一的。 # 可使用
- 概念:用于检测网站数据更新状况 - 核心机制:去重,redis的set实现去重
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule import redis from zljpro.items import ZljproItem class ZlsSpider(CrawlSpider): conn = redis.Redis(host="192.168.31.199", port=6379) name = 'zls' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.4567kan.com/index.php/vod/show/class/%E5%96%9C%E5%89%A7/id/1.html'] link = LinkExtractor(allow=r'page/\d+\.html') rules = ( Rule(link, callback='parse_item', follow=True), ) def parse_item(self, response): li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li') for li in li_list: name = li.xpath('./div/a/@title').extract_first() # 关于这里为何不使用链接提取器,使用由于rules会对每个提取到的链接都发请求,而 # 咱们增量式的意义在于只对新增的数据发起请求,因此这里咱们手动发送,并将详情页的url插 # 入redis的set中,进行去重爬取 detail_url = 'https://www.4567kan.com' + li.xpath('./div/a/@href').extract_first() # 能够将爬取过得电影的详情页的url记录起来 # if ex == 0 插入失败,若是ex == 1 插入成功 ex = self.conn.sadd("movie_detail_urls", detail_url) item = ZljproItem() item["name"] = name if ex == 1: print("正在爬取新数据") yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={"item": item}) else: print("数据暂未更新") def parse_detail(self, response): item = response.meta['item'] desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[3]/text()').extract_first() item['desc'] = desc yield item -----------------------------------------------pipeline.py class ZljproPipeline(object): def process_item(self, item, spider): conn = spider.conn conn.lpush("movieData", item) return item
- rebots - UA假装 - 验证码 - 代理 - cookie - 动态变化的请求参数 - js加密 - js混淆 - 图片懒加载 - 动态数据捕获 - selenium:规避检测