webAnalysis.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <template>
  2. <ContentWrap>
  3. <el-form
  4. class="-mb-15px"
  5. :model="queryParams"
  6. ref="queryFormRef"
  7. :inline="true"
  8. label-width="90px"
  9. >
  10. <el-form-item label="url抓取数据" prop="urls">
  11. <el-input
  12. v-model="queryParams.urls"
  13. class="!w-420px"
  14. type="textarea"
  15. :rows="1"
  16. placeholder="请输入需要爬取的页面,多个页面请用 ',' 隔开"
  17. />
  18. </el-form-item>
  19. <el-form-item>
  20. <el-button type="primary" plain :loading="loading" @click="handleExecute">执行</el-button>
  21. </el-form-item>
  22. </el-form>
  23. </ContentWrap>
  24. <ContentWrap v-if="contents.length">
  25. <el-row gutter="20">
  26. <el-col v-for="(content, index) in contents" :key="index" :span="12">
  27. <el-card class="!h-500px" v-loading="!content.data">
  28. <template #header>
  29. <div class="flex items-center justify-between">
  30. <el-text class="flex-1" truncated>{{ content.url }}</el-text>
  31. <div class="!w-85px">
  32. <Icon icon="ep:view" size="25" class="ml-10px cursor-pointer" color="#409eff" @click="showPage(content)" />
  33. <Icon icon="ep:refresh" size="25" class=" ml-18px cursor-pointer" color="#409eff" @click="handleReload(content)" />
  34. </div>
  35. </div>
  36. </template>
  37. <div v-if="content.data">
  38. <template v-if="typeof content.data === 'string'">{{ content.data }}</template>
  39. <el-tabs v-else v-model="content.tab">
  40. <el-tab-pane v-for="(v, k) in content.data.data[0]" :key="k" :label="k" :name="k" class="overflow-y-auto !h-360px">
  41. <template v-if="k === 'html'">
  42. <div class="position-sticky float-right">
  43. <el-button
  44. type="primary"
  45. class="cursor-pointer"
  46. @click="content.showHtml = !content.showHtml"
  47. :icon="SetUp"
  48. circle
  49. />
  50. </div>
  51. <pre v-if="!content.showHtml">{{ v }}</pre>
  52. <div v-else v-html="v"></div>
  53. </template>
  54. <pre v-else>{{ v || '暂无数据' }}</pre>
  55. </el-tab-pane>
  56. </el-tabs>
  57. </div>
  58. </el-card>
  59. </el-col>
  60. </el-row>
  61. </ContentWrap>
  62. <el-drawer
  63. v-model="drawer"
  64. class="!w-50vw"
  65. :with-header="false"
  66. :modal="true"
  67. >
  68. <iframe class="!w-100% !h-[calc(100vh-90px)]" :src="drawerUrl" frameborder="0"></iframe>
  69. <el-divider class="!ma-0" />
  70. <div class="position-sticky left-20px !h-50px lh-50px">
  71. <el-button type="primary" class="!w-100px" @click="drawer = false; drawerUrl = ''">关 闭</el-button>
  72. </div>
  73. </el-drawer>
  74. </template>
  75. <script setup>
  76. /** 人才采集 网页解析 */
  77. import FirecrawlApp from '@mendable/firecrawl-js'
  78. import { SetUp } from '@element-plus/icons-vue'
  79. const message = useMessage() // 消息弹窗
  80. const { t } = useI18n() // 国际化
  81. const loading = ref(false)
  82. const queryParams = reactive({
  83. urls: 'https://mp.weixin.qq.com/s/WeCRR3zN3fPvlGR4t8YFDA'
  84. })
  85. const queryFormRef = ref()
  86. const contents = ref([])
  87. const drawer = ref(false)
  88. const drawerUrl = ref('')
  89. const showPage = (content) => {
  90. drawer.value = true
  91. drawerUrl.value = content.url
  92. }
  93. const handleReload = async (content) => {
  94. content.data = null
  95. const res = await handleData(queryParams.urls)
  96. content.tab = 0
  97. content.data = res
  98. }
  99. const handleData = async (url) => {
  100. try {
  101. const app = new FirecrawlApp({ apiKey: 'fc-85c1550c6db64ce4ae8f2d2cd2606e6f' })
  102. const crawlResponse = await app.crawlUrl(url, {
  103. limit: 100,
  104. scrapeOptions: {
  105. formats: ['markdown', 'html']
  106. }
  107. })
  108. if (!crawlResponse.success) {
  109. throw new Error(`Failed to crawl: ${crawlResponse.error}`)
  110. }
  111. return crawlResponse
  112. } catch (error) {
  113. return error.message
  114. }
  115. }
  116. // 执行
  117. const handleExecute = async () => {
  118. if (!queryParams.urls) return
  119. contents.value = []
  120. const urls = queryParams.urls.split(',').map(url => url.trim()).filter(url => url)
  121. if (urls.length === 0) return
  122. urls.forEach(url => {
  123. contents.value.push({ url, tab: 'markdown', showHtml: false, data: null })
  124. })
  125. const crawlPromises = urls.map(async (url, index) => {
  126. const res = await handleData(url)
  127. contents.value[index] = { ...contents.value[index], data: res }
  128. })
  129. try {
  130. await Promise.all(crawlPromises)
  131. console.log('All crawls completed:', contents.value); // 可在此处添加成功回调
  132. } catch (error) {
  133. console.error('爬取过程中发生错误:', error);
  134. }
  135. }
  136. </script>