Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 52f0b99

Browse filesBrowse files
committed
add 自定义下载器文档
1 parent ebe1eb3 commit 52f0b99
Copy full SHA for 52f0b99

2 files changed

+301Lines changed: 301 additions & 0 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎docs/_sidebar.md‎

Copy file name to clipboardExpand all lines: docs/_sidebar.md
+1Lines changed: 1 addition & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
* [海量数据去重-dedup](source_code/dedup.md)
3939
* [报警及监控](source_code/报警及监控.md)
4040
* [监控打点](source_code/监控打点.md)
41+
* [自定义下载器](source_code/custom_downloader.md)
4142

4243
* 爬虫管理系统
4344
* [简介及部署](feapder_platform/feaplat.md)
Collapse file
+300Lines changed: 300 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# 自定义下载器
2+
3+
下载器一共分为三种:**普通下载器****支持保持session的下载器**以及**浏览器渲染下载器**。默认已经在框架中内置,setting中的配置如下
4+
5+
```
6+
DOWNLOADER = "feapder.network.downloader.RequestsDownloader" # 请求下载器
7+
SESSION_DOWNLOADER = "feapder.network.downloader.RequestsSessionDownloader"
8+
RENDER_DOWNLOADER = "feapder.network.downloader.SeleniumDownloader" # 渲染下载器
9+
```
10+
11+
- session下载器当配置中`USE_SESSION = True`时会启用
12+
- 渲染下载器当使用浏览器下载功能时会启用
13+
14+
这些下载器均为插件的形式,我们可以自定义
15+
16+
## 自定义普通下载器
17+
18+
1. 编写下载器。如在 `xxx-spider/downloader/my_downloader.py `下自定义了如下下载器
19+
20+
```
21+
import requests
22+
23+
from feapder.network.downloader.base import Downloader
24+
from feapder.network.response import Response
25+
26+
class RequestsDownloader(Downloader):
27+
def download(self, request) -> Response:
28+
response = requests.request(
29+
request.method, request.url, **request.requests_kwargs
30+
)
31+
# 将requests的response转化为feapder的Response 对象,方便后续解析时使用xpath、re等方法
32+
response = Response(response)
33+
return response
34+
```
35+
36+
注:这里返回的response对象不强制要求为是feapder的Response。返回值会传到解析函数的response参数里,若返回的是文本,则接收到的也是文本。
37+
38+
但为了代码可读性,建议将返回值转为feapder的Response后再返回。
39+
40+
转feapder的Response的方式有如下几种
41+
42+
```
43+
# 方式1
44+
# response参数为reqeusts的response
45+
Response(response)
46+
47+
# 方式2
48+
Response.from_text(text="html内容")
49+
```
50+
51+
2. 在settings中指定下载器
52+
53+
```
54+
DOWNLOADER = "downloader.my_downloader.RequestsDownloader"
55+
```
56+
57+
## 自定义session下载器
58+
59+
1. 和普通下载器一样,都是继承`Downloader`,如何保持session,可自定义。代码示例 `xxx-spider/downloader/my_downloader.py `
60+
61+
```
62+
class RequestsSessionDownloader(Downloader):
63+
session = None
64+
65+
@property
66+
def _session(self):
67+
if not self.__class__.session:
68+
self.__class__.session = requests.Session()
69+
# pool_connections – 缓存的 urllib3 连接池个数 pool_maxsize – 连接池中保存的最大连接数
70+
http_adapter = HTTPAdapter(pool_connections=1000, pool_maxsize=1000)
71+
# 任何使用该session会话的 HTTP 请求,只要其 URL 是以给定的前缀开头,该传输适配器就会被使用到。
72+
self.__class__.session.mount("http", http_adapter)
73+
74+
return self.__class__.session
75+
76+
def download(self, request) -> Response:
77+
response = self._session.request(
78+
request.method, request.url, **request.requests_kwargs
79+
)
80+
response = Response(response)
81+
return response
82+
```
83+
84+
2. 在settings中指定下载器
85+
86+
```
87+
SESSION_DOWNLOADER = "downloader.my_downloader.RequestsSessionDownloader"
88+
```
89+
90+
注意,这里要配置 `SESSION_DOWNLOADER`
91+
92+
## 自定义浏览器渲染下载器
93+
94+
1. 编写下载器 `xxx-spider/downloader/my_downloader.py `
95+
96+
**若浏览器框架本身不支持多线程,但想在多线程中使用,如playwright使用,参考如下:**
97+
98+
```
99+
import feapder.setting as setting
100+
import feapder.utils.tools as tools
101+
from feapder.network.downloader.base import RenderDownloader
102+
from feapder.network.response import Response
103+
from feapder.utils.webdriver import WebDriverPool, PlaywrightDriver
104+
105+
106+
class MyDownloader(RenderDownloader):
107+
webdriver_pool: WebDriverPool = None
108+
109+
@property
110+
def _webdriver_pool(self):
111+
if not self.__class__.webdriver_pool:
112+
self.__class__.webdriver_pool = WebDriverPool(
113+
**setting.PLAYWRIGHT, driver_cls=PlaywrightDriver, thread_safe=True
114+
)
115+
116+
return self.__class__.webdriver_pool
117+
118+
def download(self, request) -> Response:
119+
# 代理优先级 自定义 > 配置文件 > 随机
120+
if request.custom_proxies:
121+
proxy = request.get_proxy()
122+
elif setting.PLAYWRIGHT.get("proxy"):
123+
proxy = setting.PLAYWRIGHT.get("proxy")
124+
else:
125+
proxy = request.get_proxy()
126+
127+
# user_agent优先级 自定义 > 配置文件 > 随机
128+
if request.custom_ua:
129+
user_agent = request.get_user_agent()
130+
elif setting.PLAYWRIGHT.get("user_agent"):
131+
user_agent = setting.PLAYWRIGHT.get("user_agent")
132+
else:
133+
user_agent = request.get_user_agent()
134+
135+
cookies = request.get_cookies()
136+
url = request.url
137+
render_time = request.render_time or setting.PLAYWRIGHT.get("render_time")
138+
wait_until = setting.PLAYWRIGHT.get("wait_until") or "domcontentloaded"
139+
if request.get_params():
140+
url = tools.joint_url(url, request.get_params())
141+
142+
driver: PlaywrightDriver = self._webdriver_pool.get(
143+
user_agent=user_agent, proxy=proxy
144+
)
145+
try:
146+
if cookies:
147+
driver.url = url
148+
driver.cookies = cookies
149+
driver.page.goto(url, wait_until=wait_until)
150+
151+
if render_time:
152+
tools.delay_time(render_time)
153+
154+
html = driver.page.content()
155+
response = Response.from_dict(
156+
{
157+
"url": driver.page.url,
158+
"cookies": driver.cookies,
159+
"_content": html.encode(),
160+
"status_code": 200,
161+
"elapsed": 666,
162+
"headers": {
163+
"User-Agent": driver.user_agent,
164+
"Cookie": tools.cookies2str(driver.cookies),
165+
},
166+
}
167+
)
168+
169+
response.driver = driver
170+
response.browser = driver
171+
return response
172+
except Exception as e:
173+
self._webdriver_pool.remove(driver)
174+
raise e
175+
176+
def close(self, driver):
177+
if driver:
178+
self._webdriver_pool.remove(driver)
179+
180+
def put_back(self, driver):
181+
"""
182+
释放浏览器对象
183+
"""
184+
self._webdriver_pool.put(driver)
185+
186+
def close_all(self):
187+
"""
188+
关闭所有浏览器
189+
"""
190+
# 不支持
191+
# self._webdriver_pool.close()
192+
pass
193+
```
194+
195+
这里使用了WebDriverPool,参数`thread_safe=True`,即要保证使用时的线程安全,确保同个浏览器对象只能被同一个线程调用
196+
197+
**若浏览器框架本身支持多线程,如selenium,则参考如下**
198+
199+
```
200+
import feapder.setting as setting
201+
import feapder.utils.tools as tools
202+
from feapder.network.downloader.base import RenderDownloader
203+
from feapder.network.response import Response
204+
from feapder.utils.webdriver import WebDriverPool, SeleniumDriver
205+
206+
207+
class MyDownloader(RenderDownloader):
208+
webdriver_pool: WebDriverPool = None
209+
210+
@property
211+
def _webdriver_pool(self):
212+
if not self.__class__.webdriver_pool:
213+
self.__class__.webdriver_pool = WebDriverPool(
214+
**setting.WEBDRIVER, driver=SeleniumDriver
215+
)
216+
217+
return self.__class__.webdriver_pool
218+
219+
def download(self, request) -> Response:
220+
# 代理优先级 自定义 > 配置文件 > 随机
221+
if request.custom_proxies:
222+
proxy = request.get_proxy()
223+
elif setting.WEBDRIVER.get("proxy"):
224+
proxy = setting.WEBDRIVER.get("proxy")
225+
else:
226+
proxy = request.get_proxy()
227+
228+
# user_agent优先级 自定义 > 配置文件 > 随机
229+
if request.custom_ua:
230+
user_agent = request.get_user_agent()
231+
elif setting.WEBDRIVER.get("user_agent"):
232+
user_agent = setting.WEBDRIVER.get("user_agent")
233+
else:
234+
user_agent = request.get_user_agent()
235+
236+
cookies = request.get_cookies()
237+
url = request.url
238+
render_time = request.render_time or setting.WEBDRIVER.get("render_time")
239+
if request.get_params():
240+
url = tools.joint_url(url, request.get_params())
241+
242+
browser: SeleniumDriver = self._webdriver_pool.get(
243+
user_agent=user_agent, proxy=proxy
244+
)
245+
try:
246+
browser.get(url)
247+
if cookies:
248+
browser.cookies = cookies
249+
# 刷新使cookie生效
250+
browser.get(url)
251+
252+
if render_time:
253+
tools.delay_time(render_time)
254+
255+
html = browser.page_source
256+
response = Response.from_dict(
257+
{
258+
"url": browser.current_url,
259+
"cookies": browser.cookies,
260+
"_content": html.encode(),
261+
"status_code": 200,
262+
"elapsed": 666,
263+
"headers": {
264+
"User-Agent": browser.user_agent,
265+
"Cookie": tools.cookies2str(browser.cookies),
266+
},
267+
}
268+
)
269+
270+
response.driver = browser
271+
response.browser = browser
272+
return response
273+
except Exception as e:
274+
self._webdriver_pool.remove(browser)
275+
raise e
276+
277+
def close(self, driver):
278+
if driver:
279+
self._webdriver_pool.remove(driver)
280+
281+
def put_back(self, driver):
282+
"""
283+
释放浏览器对象
284+
"""
285+
self._webdriver_pool.put(driver)
286+
287+
def close_all(self):
288+
"""
289+
关闭所有浏览器
290+
"""
291+
self._webdriver_pool.close()
292+
```
293+
294+
2. 在settings中指定下载器
295+
296+
```
297+
RENDER_DOWNLOADER = "downloader.my_downloader.MyDownloader"
298+
```
299+
300+
注,这里要写`RENDER_DOWNLOADER`

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.