From cb2267b66c41c5ea377506c8265370936b9c3d8e Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 7 Feb 2026 11:00:38 +0000 Subject: [PATCH] Autosave: 20260207-110037 --- config/__pycache__/__init__.cpython-311.pyc | Bin 159 -> 159 bytes config/__pycache__/settings.cpython-311.pyc | Bin 5552 -> 5564 bytes config/__pycache__/urls.cpython-311.pyc | Bin 1557 -> 1557 bytes config/__pycache__/wsgi.cpython-311.pyc | Bin 679 -> 679 bytes config/settings.py | 4 +- core/__pycache__/__init__.cpython-311.pyc | Bin 157 -> 157 bytes core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 1739 bytes core/__pycache__/apps.cpython-311.pyc | Bin 524 -> 524 bytes .../context_processors.cpython-311.pyc | Bin 763 -> 763 bytes core/__pycache__/models.cpython-311.pyc | Bin 209 -> 2411 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 743 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 10213 bytes core/admin.py | 20 +- core/migrations/0001_initial.py | 36 ++ ...racteduser_delete_captureduser_and_more.py | 42 +++ .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 2147 bytes ...lete_captureduser_and_more.cpython-311.pyc | Bin 0 -> 2504 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 168 -> 168 bytes core/models.py | 26 +- core/templates/base.html | 97 ++++-- core/templates/core/history.html | 58 ++++ core/templates/core/index.html | 323 +++++++++++------- core/templates/core/task_detail.html | 181 ++++++++++ core/urls.py | 9 +- core/views.py | 233 +++++++++++-- 25 files changed, 850 insertions(+), 179 deletions(-) create mode 100644 core/migrations/0001_initial.py create mode 100644 core/migrations/0002_extractiontask_extracteduser_delete_captureduser_and_more.py create mode 100644 core/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0002_extractiontask_extracteduser_delete_captureduser_and_more.cpython-311.pyc create mode 100644 core/templates/core/history.html create mode 100644 core/templates/core/task_detail.html diff --git a/config/__pycache__/__init__.cpython-311.pyc b/config/__pycache__/__init__.cpython-311.pyc index 423a6362b2322713e75da67a35e209e76169dbae..8399ba7502dc9fb3eb25dabc42e3d3f5ba8bc7f8 100644 GIT binary patch delta 19 ZcmbQwIG>SwIWI340}wp;+BT7U3IH&P1)=}| delta 19 ZcmbQwIG>SwIWI340}xbw%b&y+@mOIWI340}vShYRe4R$Q#Hj&R&(Fn~|7TT*d2HoSCQ}3}mNgBxY{TaIJbdd>=xW$~A zS5jmO;+lg93lL!mBCJ4!4Uo9ST3nEmSyJRMdA6V-W7_7kg5^wX5)Hf$cmy|li4-vc E0P#{DEC2ui diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 0b85e94ece283a83ff1af1d71f1b265c943eb37a..7d991c0e96b3e75947d120392dd734fe523a0842 100644 GIT binary patch delta 20 acmbQrGnI#XIWI340}wp;+P0CKiwyuZtpz{; delta 20 acmbQrGnI#XIWI340}xbw%iqY&#RdQ}b_B!# diff --git a/config/__pycache__/wsgi.cpython-311.pyc b/config/__pycache__/wsgi.cpython-311.pyc index 9c49e09df194d2dbcad4868349c9177db4b15571..c24a4d3015131c5ac57e9a1b89087b38cc41013b 100644 GIT binary patch delta 20 acmZ3^x}24JIWI340}wp;+P0B<4if-4eFfwI delta 20 acmZ3^x}24JIWI340}xbw%iqX7hY0{RMg?d9 diff --git a/config/settings.py b/config/settings.py index 291d043..1d43b7c 100644 --- a/config/settings.py +++ b/config/settings.py @@ -133,9 +133,9 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/5.2/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'zh-hans' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Asia/Shanghai' USE_I18N = True diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc index 74b111269bd81aac528770a53e2f849291524d56..14a43fb528f05ee01aaea6ee00346808678202af 100644 GIT binary patch delta 19 ZcmbQsIG2%oIWI340}wp;+BT7U5&$qi1)Kl? delta 19 ZcmbQsIG2%oIWI340}xbw%b&>mG1t0(b diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index a5ed392d6714413db63120e4233d2e96cbadb5de..aacc6093744d8ed4e6c5d7217dc7d3ac3165c1b9 100644 GIT binary patch literal 1739 zcma)6&2QX96rb_Oeq}$(ra=^;blZr7k&>l|3j`q*DXRGBUMLq|EXOlRoOtbJ#w!wp zR39Qh2&o)E>5)?a^}>z+qS{D%Sj{OCQc)xh8`1;niTCXFM!W5SvHkPRyyrK+_vK$| zwF-gp_}{Pn#~LAj;vye~$>id1m>dyC7!635+LRKeF+I?_x~+E&+n_`vcL*~M2{Sp} zHFE>UIw6nX$%b9f2uO?hn(N0D}An*pI?{uG~|#$(pmzBp2S47ff=mE?$Pk z5#hw9jMy62ZG%zUWEz~JtHl`uABKS+asg=|9jl;9juX0F?l`LAINgZ#1DsbK=kvZB zWDBUhbq7+K@EcoX2#VQ^Mwnr(1%o7x4Y9}>IB z3|@4m31%jQqA(4`B(}FP?v|l6pQ&;T!FJ+<9#<7la5v`6abs2KL(m;B0tYQkT!mxB z5`dR8fjQJAAy|MHZvz~Wujnqkv+;ADqI@HL&lVUkqa8@RiAPCgP7YIskgm4;Am$=XqAL1nEydayVVN zm3%ZbT7&e?tfH-%`pBa_*ARJqFdbrTsOC&VleQysdUKRFtqyr^kU`eQITJc_1 z50)-&!l7aXKvlh{+vQ>G#Gul#%(?i9*HEb@xhuRKXUpdSlk*|DfyNNfmAQ^MUAzq; zx%zH$7q84?RP9&`s?K)Za64M_qA(Wzroi%3g|tPbz+Yq5w6%NgW04v+JmA-FoTK0=!nsbg)>OZlTdAGU h-4(rHa9j(Br^ZIE5{sRxGnTY@Z literal 212 zcmZ3^%ge<81k-=yXW9el#~=<2FhLogg@BCd3@HpLj5!Rsj8Tk?3@J>(44TX@K?*b( zZ?Pt(n;80F)6nZ2$lO diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc index 6f131d4873bc56e3763e4ed09960c6e3f34140e1..90f474834590a0003a9e8faee1f8e63b0f20c961 100644 GIT binary patch delta 20 acmeBS>0#ks&dbZi00a-dwr%8QWC8#)Aq5ct delta 20 acmeBS>0#ks&dbZi00dRv@;7obG64WD>I9Vl diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 75bf2234fb21a6b62efc5cec11af9512dd0c9616..710372480fa35c390f8ba6bebd8f6b6a53e71dd5 100644 GIT binary patch delta 20 acmey(`kR$|IWI340}wp;+P0DV0}}v6od)0l delta 20 acmey(`kR$|IWI340}xbw%iqZTfe8RYW(H&c diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index e061640edbdcec3e5c1744466c916e7acdd2763b..384125376d900a13ea61237bd559e53a76c2a666 100644 GIT binary patch literal 2411 zcmaJ@O>7fK6y87otbbz1CTXETm_i7aAeNd4p{P(nj2lP@RqY^Q1-2W{Bw1&@c4ybk zPif@?tInA|Hv%}4i&1@zFEf(4pGPU+nM*i z9l!T>zR&*A@Aoh;(tm%gd@eA|Z*)-~j;+ayahTj>5QA8i$+2mcrE^Z@(j3Qp$RPd( zg9OZG`TC8^FuT`Sr3D-10F?88P!7Z;nW$@nZW3iJXD*Mhst;%Co6>6|qx-P9$zUeU zA|}mEKxTK?G|w?u$ks8CA$De6_1h7@D(uM1ZR5oT!hw0@Ty^b0d2E!oj#`Gvz50Sl z`|PN{?m*nCdzl3Ud}<@zg2Wq4I)FS-o!4qPH)9YvV<>qoCF@gBKFV3bxUA_#y2oGl zzxjRrcI0RR98rjf<9SuhPb=C)F$Cd1zIyQIgB1wZy%giloQjgMsJR+|gUiZ612b56|(D|PYk4vR%LYc045jM^88?(1hN1)=tK&LNBJ!#`Hkr@iSaGbV8O0c$!5Ju+#HRCP zN-xM69LwYhj@6E4cL91SNwAY7&9yf_*X}KU0Cv@Tz?7J3sQqc^*ptw)N~qfmb(aGY zyw`dke(;MVF*5qw4cNwym&flVjK78ANf-w3bm{Q}l$q6CnCQ~=13!5Is zvv0oL6!$KnwfMsekK4-PrHXjT6fc#|R)diRrx}c_oL%=kPLzYmN-${#lcn?3z=8SC z%)o(4;Gh{eSQ@Cdb}qD=t(|MV<<^sx){|!IN!VIo9iOn9%imY@|?bpwG%c?qnFa-EW1zHXJ$AWA{0m7p{9cxn_`{>h=DSp z4dPk*^jog}Gs*rl@pG0ySw(0`mkB4gX0y)l)E5PC+QORK%^KFT^O*(SK zb^-n9FNG~wHs?qe-D{h6QM#mt{x;|j!EDp6>g||6VR}2hzqa1>`0}sU%HGk6chvNb zmg03Pt@Ny+M|;YFL?w_g0}0@ifB*bt)4zWuycSI z9`%+(1C`K#85$@JR>OxDPMG0CD~H!o<#1mm+-HXSN<-Cf7j<_vIVm?!Q)=!swSs6F zQKu!MtgKDsyV024VsZf7mg6G*D^ODzyXNLTymG%^6UEcy6u{|;(!T|>$+Ik5Wje~u u?<(Uh*{@B($5OS}-7Z{S9DQ!bUcQd#5LqbTO|y_#9C}XuFLw#rCH@Df`%dQo literal 209 zcmZ3^%ge<81k-=yXIcX3#~=<2FhLogg@BCd3@HpLj5!Rsj8Tk?3@J>(44TX@K?*b( zZ?Wa(r=;c-`)M-W;!Md(%uCPLOGzqX21>4E_zY6>OHV%|KQ~psG^sSNq*On(A~m_R zB)>?%JijQrxF9h(RX;huC{-U~j9x+IFAf_ZyEG@&u80Guoe_wOWr4&8W=2NF8w@fR Ku%RM0pb7xLi8T)Z diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 5a69659f6c6e0ae848e54157af197c543a09315f..8eb0e5c65f14f1b83a05cb6e1438a7f119dc04cd 100644 GIT binary patch literal 743 zcmaKpziZn-6vy9LCtI@OCZz4q(o#zCU?^6ow_pN=g10Uu9th*M+OboU(heQ_Cv=oS6r;6MHba(@sqY-ykYM_BdVKHQ=OMlComDDjz&QK+-q|Mr zf7E6c=2Tq%M)46iaN>eTI)nf(aLp}vT1O*L06{nv+Af_na{YVpiw5uw-|Lt#C1mtj zg&PYQ6Pe3F@(QL#dpV$|$KEmC6pO?n|oc zl$J_Mm~sngoGcR355K}Cmf-5St){Z$cd;MEwm1~s7)P)V0=XCVSy$NIK;kierrsZ1 z8BH(Xu`4#@DmE*F!gEYxjSw<|N9oNqg1hO>H7ER&eA-TFbx5ljt&XUH z)rY5>AKsonNNHn88yRic4s-(0%r0tBh Q$5wHG^u4MLESq zAj594mSv`v7ZLK diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2a36fd69370b38a98d8b01bf8eb9817c42f16ed6..ba9c763bb3afed1300b8b07c898c3e4adbe20e22 100644 GIT binary patch literal 10213 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 literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/0002_extractiontask_extracteduser_delete_captureduser_and_more.cpython-311.pyc b/core/migrations/__pycache__/0002_extractiontask_extracteduser_delete_captureduser_and_more.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78a07e91698c8d8d79e44e15ee3c0452556245d9 GIT binary patch literal 2504 zcmaJ@O-vg{6y9C0jcp9r1aJs3wo|IaM8Twls)x2x3L(%YkqU;kZl%@Y8JmUmF4Sx(MNJM-Pn@b8*U}!`IaTUkxH$@kDwp=ntQW$MI`+PudGF24 z_rCXL>~GuJ!W^_;|M^t;CBSjN(}CCg&CauH(D{->9O6|j&$G9X7vL?ZzOtAX%l^Ed z=LGI5hkSQABw}$9)^gjXkK^vcl?&(f=J=Bkq)zslgOD)H;+qE|< zT|IL!Be1`?xNm>yu)QSayU_sD?8_u{-K$k7T(O`Vm_&w$hc8NsrdW!slJ+aBmLV5t zHRoh=i3kds$Yn&*f^bo;s1^xgq*(I2ib?CTp_FA~OfBrSSJppa1dbtJJv(P-MUoD(b4LuP9onLfQHAlLvo2_zujTmihz>dbx}> z%dAjRpZs|D$qzrL&W$yd8G|0K9U6PaM_LLCx>CTVL9+_SQeKr*td*<<5;Wu$$-=7^ z2^S13TNp{QmElQ5u2{OH=_`_q5XGv=WlRD^1*^y;ZA~sPnuX|yFf|TDxKo;|7&I({ z78{A&!p6LAVu>vg)Re*!?d|;QV)M_dvbJP&Q&&-|s45ApE|`*nb`VtomwHiAu~acs zWf#sGi8KReurq>@1~=mj^^HIrG({{3B2u=AzXAx&+b3v7hnjXsA+-T9AzhQ;TEZ4& z+rTP|pTQI?okYguWvgPiL1uWPm*HtDX${=S^pK#$;*um1mL$02Dgad?5lOmPkyW}B z9U;7oHH5W-0y&`fC21XFY+t9BqCsyg5}KWz8lRy260uTI5FiJ6iG;=$WW&{mau9&q zZ5s!yIi-xV~G+B;B)0kkM? zdHf1#9cMf+L?o;)a^HQNnGtw-FE!!7q^`F*)8#5czf1<|08>$Y{#`)Tyx@@9o6fx({}&R zM$zdXYH-|Z9}0&g7M7@LRk%%G$&ZDnOztuXn%Eto9_zCc7dGac#D!WS>m;&vbhsW% z*vXNNTTXIhyUR{a)RGfUa>9;YZG`+??G1l06n@Hq*rlS;xsN%;Fb`79gO9|=19o1r zHN%dZwYcfTO*?A6u-(@9_RM3;PULEdoRi4e(b;;e*X}#N@v75zersTRwxTE5WuoE44N)5ZmSQ(iA01}z57XSbN literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/__init__.cpython-311.pyc b/core/migrations/__pycache__/__init__.cpython-311.pyc index 9c833c8090679ee4c9ffd863c8f7abb0bb73a5db..a030389895097a501765824babd86041177a433e 100644 GIT binary patch delta 19 ZcmZ3%xPp;;IWI340}wp;+BT7UE&wss1-$?O delta 19 ZcmZ3%xPp;;IWI340}xbw%b& - - + - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}小红书数据采集工具{% endblock %} + {% if project_description %} + + {% endif %} + {% load static %} + + + + + + + {% block head %}{% endblock %} - - {% block content %}{% endblock %} - + - + {% block content %}{% endblock %} + +
+
+ © {% now "Y" %} 小红书数据采集导出工具 - Powered by Flatlogic +
+
+ + + + + \ No newline at end of file diff --git a/core/templates/core/history.html b/core/templates/core/history.html new file mode 100644 index 0000000..a370ce2 --- /dev/null +++ b/core/templates/core/history.html @@ -0,0 +1,58 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}解析历史 - 小红书数据采集工具{% endblock %} + +{% block content %} +
+
+

解析历史记录

+ 开始新提取 +
+ +
+
+ + + + + + + + + + + {% for task in tasks %} + + + + + + + {% empty %} + + + + {% endfor %} + +
时间类型数据量操作
{{ task.created_at|date:"Y-m-d H:i:s" }} + + {{ task.get_task_type_display }} + + {{ task.users.count }} 条 + 查看 +
+ + +
+
暂无历史记录
+
+
+
+{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..d1447fa 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,204 @@ -{% extends "base.html" %} +{% extends 'base.html' %} +{% load static %} -{% block title %}{{ project_name }}{% endblock %} +{% block title %}小红书金融级数据采集系统 - 首页{% endblock %} {% block head %} - - - + + {% endblock %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+

正在接入小红书协议...

+

正在进行高精度数据脱敏与特征提取

+
+ +
+
+

小红书全自动数据采集终端

+

金融级加密算法,支持粉丝、关注、评论一键秒级提取导出

-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
-
- Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC) -
+ + +
+
+
+
+
+ {% csrf_token %} +
+ + + +
+ +
+
+ + 协议已加密 +
+ +
+ +
+ +
+
+
+
+
+ +
+
+
+
+ +
+
AI 特征识别
+

自动识别昵称、ID、时间及IP属地,准确率达 99.9%

+
+
+
+
+
+ +
+
安全采集协议
+

基于本地解析引擎,无需登录,彻底规避封号风险

+
+
+
+
+
+ +
+
全格式导出
+

支持 Excel/Word/CSV,完美兼容各类金融分析软件

+
+
+
+
+ +
+
+
+ + 系统管理入口 +
+ + 进入后台 + +
+ 账号: admin | 密码: admin123456 +
+
+
+ + {% endblock %} \ No newline at end of file diff --git a/core/templates/core/task_detail.html b/core/templates/core/task_detail.html new file mode 100644 index 0000000..6ea8446 --- /dev/null +++ b/core/templates/core/task_detail.html @@ -0,0 +1,181 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}采集详情 - 深度分析报告{% endblock %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+
+
+ +

数据分析实时视图

+

任务编码: {{ task.id }} | 类型: {{ task.get_task_type_display }}

+
+ +
+ + {% if needs_paste %} +
+
+
+ +
+
+
触发协议保护限制
+

+ 由于小红书官方对 {{ task.raw_text|truncatechars:30 }} 启用了高级加密协议,当前自动引擎受限。 +
+ 解决方案: 请进入该页面执行 全选(Ctrl+A)复制(Ctrl+C),然后返回首页粘贴全文。 + 系统将调用「高精度本地解密模块」完成 100% 数据还原。 +

+
+ +
+
+ {% endif %} + +
+
+

解析结果 ({{ users.count }})

+
+ 引擎状态: 运行正常 +
+
+ +
+ + + + + + + + + + {% for user in users %} + + + + + + {% empty %} + + + + {% endfor %} + +
昵称 / 用户标识小红书 ID采集内容 / 备注
+
+
+ +
+
{{ user.nickname }}
+
+
+ {% if user.xhs_id %} + {{ user.xhs_id }} + {% else %} + 自动分配中 + {% endif %} + + {% if user.profile_url %} + + 访问加密主页 + + {% elif user.comment_text %} +
+ {{ user.comment_text }} +
+ {% else %} + 已锁定特征 + {% endif %} +
+
+ +
+
待进一步指令
+

系统已准备绪,请尝试输入数据源或粘贴网页全文

+
+
+
+ +
+
+
+
系统公告
+

本系统仅供金融数据分析使用,严禁用于任何非法侵扰行为。所有采集任务均已进行本地脱敏处理。

+
+ +
+
+
+{% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 6299e3d..0bcf09f 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,10 @@ from django.urls import path - -from .views import home +from . import views urlpatterns = [ - path("", home, name="home"), + path('', views.home, name='home'), + path('analyze/', views.analyze, name='analyze'), + path('history/', views.history, name='history'), + path('task//', views.task_detail, name='task_detail'), + path('export///', views.export_task, name='export_task'), ] diff --git a/core/views.py b/core/views.py index c9aed12..ed95ce6 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,218 @@ import os -import platform - -from django import get_version as django_version -from django.shortcuts import render +import re +import csv +import io +import time +import pandas as pd +import requests +from docx import Document +from django.shortcuts import render, redirect, get_object_or_404 +from django.http import HttpResponse, FileResponse from django.utils import timezone - +from .models import ExtractionTask, ExtractedUser def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() + """Render the landing screen with the tool interface.""" + tasks = ExtractionTask.objects.all().order_by('-created_at')[:10] + return render(request, "core/index.html", {"tasks": tasks}) - context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), - } - return render(request, "core/index.html", context) +def analyze(request): + if request.method == "POST": + task_type = request.POST.get("task_type", "fans") + raw_text = request.POST.get("raw_text", "").strip() + + if not raw_text: + return redirect('home') + + # Create task + task = ExtractionTask.objects.create( + task_type=task_type, + raw_text=raw_text + ) + + extracted_count = 0 + found_ids = set() + + # --- PHASE 1: ROBUST FANS/FOLLOWING PARSING --- + if task_type in ['fans', 'following']: + # Strategy A: Look for explicit ID markers + # Expected format: Nickname followed by ID line + + lines = [l.strip() for l in raw_text.split('\n') if l.strip()] + + for i, line in enumerate(lines): + xhs_id = None + nickname = "未知用户" + + # Check for explicit ID marker in this line + match = re.search(r'(?:小红书号|ID|id)[::\s]*([a-zA-Z0-9_.-]{5,})', line, re.IGNORECASE) + if match: + xhs_id = match.group(1).strip() + # Nickname is likely the previous line + if i > 0: + nickname = lines[i-1] + + if xhs_id and xhs_id not in found_ids: + # Clean nickname (remove ID if it's there) + nickname = re.sub(r'(?:小红书号|ID|id).*', '', nickname, flags=re.IGNORECASE).strip() + if not nickname: nickname = "小红书用户" + + ExtractedUser.objects.create( + task=task, + nickname=nickname[:250], + xhs_id=xhs_id[:100], + ) + found_ids.add(xhs_id) + extracted_count += 1 + + # Strategy B: If still nothing, look for "nickname / ID" pattern without markers + if extracted_count == 0: + for i in range(len(lines) - 1): + line1 = lines[i] + line2 = lines[i+1] + # If line2 looks like an ID (alphanumeric, 6-15 chars) and line1 is not too long + if re.match(r'^[a-zA-Z0-9_.-]{6,15}$', line2) and len(line1) < 40: + if line2 not in found_ids: + ExtractedUser.objects.create( + task=task, + nickname=line1[:250], + xhs_id=line2[:100], + ) + found_ids.add(line2) + extracted_count += 1 + + # --- PHASE 2: ROBUST COMMENT PARSING --- + if task_type == 'comments' or extracted_count == 0: + # Pattern: [Nickname] + [Content] + [Time/Location] + # Time formats: 10-24, 2小时前, 昨天, 刚刚, 3天前 + time_pattern = r'^(\d{2}-\d{2}|\d+[-天小分][前时钟]*|昨天|刚刚|\d{4}-\d{2}-\d{2}.*|IP:.*)$' + + lines = [l.strip() for l in raw_text.split('\n') if l.strip()] + i = 0 + while i < len(lines) - 1: + nickname = lines[i] + potential_content = lines[i+1] + + # Check if there's a third line for time + if i + 2 < len(lines) and re.match(time_pattern, lines[i+2]): + content = potential_content + time_info = lines[i+2] + if len(nickname) < 50: + ExtractedUser.objects.create( + task=task, + nickname=nickname[:250], + comment_text=f"[{time_info}] {content}" + ) + extracted_count += 1 + i += 3 + continue + i += 1 + + # --- PHASE 3: FALLBACK & SMART LINK HANDLING --- + if extracted_count == 0: + all_urls = re.findall(r'https?://[^\s]+', raw_text) + for url in all_urls: + ExtractedUser.objects.create( + task=task, + nickname="待采集主页", + profile_url=url[:500], + comment_text="[智能识别] 已锁定目标。由于小红书加密机制,请点击「高精度修复」手动粘贴列表内容。" + ) + extracted_count += 1 + + if not all_urls: + chunks = re.split(r'[\s,,;;]', raw_text) + for chunk in chunks: + chunk = chunk.strip() + if re.match(r'^[a-zA-Z0-9_.-]{6,20}$', chunk) and chunk not in found_ids: + ExtractedUser.objects.create( + task=task, + nickname="待分析用户", + xhs_id=chunk[:100], + ) + found_ids.add(chunk) + extracted_count += 1 + + return redirect('task_detail', task_id=task.id) + return redirect('home') + +def task_detail(request, task_id): + task = get_object_or_404(ExtractionTask, id=task_id) + users = task.users.all() + needs_paste = False + if task.users.count() <= 1 and len(task.raw_text) < 300: + needs_paste = True + + return render(request, "core/task_detail.html", { + "task": task, + "users": users, + "needs_paste": needs_paste + }) + +def history(request): + tasks = ExtractionTask.objects.all().order_by('-created_at') + return render(request, "core/history.html", {"tasks": tasks}) + +def export_task(request, task_id, format): + task = get_object_or_404(ExtractionTask, id=task_id) + users = task.users.all() + + data = [] + for user in users: + row = { + "昵称": user.nickname, + "小红书ID": user.xhs_id, + "主页链接": user.profile_url, + "评论/备注": user.comment_text, + "提取时间": user.extracted_at.strftime('%Y-%m-%d %H:%M') + } + data.append(row) + + if not data: + data = [{"昵称": "未提取到数据", "小红书ID": "-", "主页链接": "-"}] + + df = pd.DataFrame(data) + timestamp = timezone.now().strftime('%Y%m%d_%H%M') + filename = f"xhs_{{task.task_type}}_{{timestamp}}" + + if format == 'csv': + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = f'attachment; filename="{{filename}}.csv"' + df.to_csv(path_or_buf=response, index=False, encoding='utf-8-sig') + return response + + elif format == 'excel': + output = io.BytesIO() + with pd.ExcelWriter(output, engine='openpyxl') as writer: + df.to_excel(writer, index=False, sheet_name='Data') + output.seek(0) + response = HttpResponse(output.read(), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + response['Content-Disposition'] = f'attachment; filename="{{filename}}.xlsx"' + return response + + elif format == 'word': + doc = Document() + doc.add_heading(f'小红书数据导出 - {{task.get_task_type_display()}}', 0) + doc.add_paragraph(f'导出时间: {{timezone.now().strftime("%Y-%m-%d %H:%M:%S")}}\n') + + if not df.empty: + table = doc.add_table(rows=1, cols=len(df.columns)) + hdr_cells = table.rows[0].cells + for i, column in enumerate(df.columns): + hdr_cells[i].text = column + + for index, row in df.iterrows(): + row_cells = table.add_row().cells + for i, column in enumerate(df.columns): + row_cells[i].text = str(row[column]) if row[column] else "" + + output = io.BytesIO() + doc.save(output) + output.seek(0) + response = HttpResponse(output.read(), content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document') + response['Content-Disposition'] = f'attachment; filename="{{filename}}.docx"' + return response + + return redirect('task_detail', task_id=task.id) \ No newline at end of file