Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save shikelong/4043c64d11b4fcbbe79b96f0fb86a2ca to your computer and use it in GitHub Desktop.
Save shikelong/4043c64d11b4fcbbe79b96f0fb86a2ca to your computer and use it in GitHub Desktop.
Investment helper / script
//Help me to write a javascript function that download my investment data from a website.
// What the function need to do is parse the HTML of the page and extract the investment data.
// The description of the DOM structure:
// the title row's select is .PositionList_listTitle, then the col name defined under the span in the class ListItemView_commonBox, need to ignore .ListItemView_detailExtra
// the data row's select is ListItemView, but it is just a wrapper. the value of each cols is in .ListItemView_commonBox, but .ListItemView_commonBox may not
// the direct child of the row, it may exists some nest structure.
// If you find the .ListItemView_commonBox, then you can find the text in the span, but sometimes there are some nested spans.
// let's me give you some example:
/*
* This is the html of the title row */
/*
<div class="PositionList_listTitle" id="positionListCalContentID"><div class="ListItemView_fixedExtra FixedTitleBg" style="position: sticky; left: 0px; z-index: 1;"><div class="ListItemView_commonBox ListItemView_detailExtra"><div class="PositionList_titleStyle">明细</div></div><div class="ListItemView_commonBox PositionListTitleBox ListItemView_codeExtra paddingLeft16 code"><div class="PositionList_titleStyle CPointer "><span>代码</span></div></div><div class="ListItemView_commonBox PositionListTitleBox ListItemView_nameExtra paddingLeft16 name"><div class="PositionList_titleStyle CPointer "><span>名称</span></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 cwzb"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>仓位占比</span></div></div><div class="ListItemView_sortIconOuter"><div class="displayFlex " style="width: 12px; height: 12px;"><img src=""></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 cyje"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>持有金额</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 cyyk"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>持有盈亏</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 cyykl"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>持有盈亏率</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 ljyk"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>累计盈亏</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 ljykl"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>累计盈亏率</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 drykl"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>当日盈亏率</span></div><div class="ListItemView_dateLabel">04-10</div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 bzyk"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>本周盈亏</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 byyk"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>本月盈亏</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 jnyk"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>今年盈亏</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 ccts"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>持仓天数</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 zf"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>当日涨幅</span></div><div class="ListItemView_dateLabel">04-10</div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 hbzf"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>回本涨幅</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 jyyzf"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>近1月涨幅</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 jsyzf"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>近3月涨幅</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 jlyzf"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>近6月涨幅</span></div></div></div></div><div role="button" tabindex="0" aria-disabled="false" aria-roledescription="sortable" aria-describedby="DndDescribedBy-31" style="opacity: 1; font-size: 14px; line-height: 14px;"><div class="ListItemView_commonBox PositionListTitleBox paddingRight16 jynzf"><div class="ListItemView_titleDateContainer"><div class="CPointer" style="width: 100%; font-size: 14px; line-height: 14px; color: rgb(51, 51, 51); font-weight: 400; display: flex; justify-content: flex-end;"><span>近1年涨幅</span></div></div></div></div><div id="DndDescribedBy-31" style="display: none;">
To pick up a draggable item, press the space bar.
While dragging, use the arrow keys to move the item.
Press space again to drop the item in its new position, or press escape to cancel.
</div><div id="DndLiveRegion-31" role="status" aria-live="assertive" aria-atomic="true" style="position: fixed; width: 1px; height: 1px; margin: -1px; border: 0px; padding: 0px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); clip-path: inset(100%); white-space: nowrap;"></div></div>
*/
/**
* This is the example of one data row
*/
/**
*
* <div class="ListItemView ListItemView_itemWithHover "><div class="ListItemView_fixedExtra FixedItemBg ListItemView_fixedHover " style="position: sticky; left: 0px; z-index: 1;"><div class="ListItemView_detailIconBox ListItemView_detailExtra"><div class="ListItemView_detail_icon"></div></div><div class="ListItemView_commonBox ListItemView_codeExtra "><span class="ListItemView_codeNameWithHover">001316</span></div><div class="ListItemView_commonBox ListItemView_nameExtra "><div class="ListItemView_columnBox "><div class="overflowDian displayFlex" style="align-items: center;"><span class="ListItemView_codeNameWithHover overflowDian ">安信稳健增值混合A</span></div><img class="ListItemView_updatedIcon" src=""></div></div></div><div class="ListItemView_commonBox cyje false paddingRight16" style="order: 1; justify-content: flex-end;"><div class="ListItemView_titleDateContainer"><div class=" "><span>40411.88</span></div></div></div><div class="ListItemView_commonBox drykl false paddingRight16" style="display: flex; flex-direction: column; order: 6; justify-content: center; align-items: flex-end;"><div class=" profitFont"><span>+<span>0.33<span class="sylUnit">%</span></span></span></div></div><div class="ListItemView_commonBox cyyk false paddingRight16" style="order: 2; justify-content: flex-end;"><div class="ListItemView_titleDateContainer"><div class=" profitFont"><span>+3919.03</span></div></div></div><div class="ListItemView_commonBox cyykl false paddingRight16" style="display: flex; flex-direction: column; order: 3; justify-content: center; align-items: flex-end;"><div class=" profitFont"><span>+<span>10.74<span class="sylUnit">%</span></span></span></div></div><div class="ListItemView_commonBox ljyk false paddingRight16" style="order: 4; justify-content: flex-end;"><div class="ListItemView_titleDateContainer"><div class=" profitFont"><span>+4449.53</span></div></div></div><div class="ListItemView_commonBox ljykl false paddingRight16" style="display: flex; flex-direction: column; order: 5; justify-content: center; align-items: flex-end;"><div class=" profitFont"><span>+<span>10.74<span class="sylUnit">%</span></span></span></div></div><div class="ListItemView_commonBox bzyk false paddingRight16" style="order: 7; justify-content: flex-end;"><div class="ListItemView_titleDateContainer"><div class=" lossFont"><span>-200.07</span></div></div></div><div class="ListItemView_commonBox byyk false paddingRight16" style="order: 8; justify-content: flex-end;"><div class="ListItemView_titleDateContainer"><div class=" lossFont"><span>-223.61</span></div></div></div><div class="ListItemView_commonBox jnyk false paddingRight16" style="order: 9; justify-content: flex-end;"><div class="ListItemView_titleDateContainer"><div class=" lossFont"><span>-225.96</span></div></div></div><div class="ListItemView_commonBox cwzb false paddingRight16" style="display: flex; flex-direction: column; order: 0; justify-content: center; align-items: flex-end;"><div class=" "><span><span>10.26<span class="sylUnit">%</span></span></span></div></div><div class="ListItemView_commonBox zf false paddingRight16" style="display: flex; flex-direction: column; order: 11; justify-content: center; align-items: flex-end;"><div class=" profitFont"><span>+<span>0.30<span class="sylUnit">%</span></span></span></div></div><div class="ListItemView_commonBox ccts false paddingRight16" style="order: 10; justify-content: flex-end;"><div class="ListItemView_titleDateContainer"><div class=" "><span>1309</span></div></div>天</div><div class="ListItemView_commonBox hbzf false profitFont paddingRight16" style="display: flex; flex-direction: column; order: 12; justify-content: center; align-items: flex-end;">--</div><div class="ListItemView_commonBox jyyzf false paddingRight16" style="display: flex; flex-direction: column; order: 13; justify-content: center; align-items: flex-end;"><div class=" lossFont"><span><span>-0.29<span class="sylUnit">%</span></span></span></div></div><div class="ListItemView_commonBox jsyzf false paddingRight16" style="display: flex; flex-direction: column; order: 14; justify-content: center; align-items: flex-end;"><div class=" lossFont"><span><span>-0.02<span class="sylUnit">%</span></span></span></div></div><div class="ListItemView_commonBox jlyzf false paddingRight16" style="display: flex; flex-direction: column; order: 15; justify-content: center; align-items: flex-end;"><div class=" profitFont"><span>+<span>1.71<span class="sylUnit">%</span></span></span></div></div><div class="ListItemView_commonBox jynzf false paddingRight16" style="display: flex; flex-direction: column; order: 16; justify-content: center; align-items: flex-end;"><div class=" profitFont"><span>+<span>5.61<span class="sylUnit">%</span></span></span></div></div></div>
*/
// Please use the DOM sample as source of truth.
function downloadAsCSV() {
// Extract column headers and map them to their class names
const titleRow = document.querySelector(".PositionList_listTitle");
const headerMap = Array.from(
titleRow.querySelectorAll(".ListItemView_commonBox")
)
.filter((box) => !box.closest(".ListItemView_detailExtra"))
.reduce((map, box, index) => {
const span = box.querySelector("span");
if (span) {
const className = Array.from(box.classList).find((cls) =>
/^[a-z]+$/.test(cls)
); // Find the class name matching the pattern
if (className) {
map[className] = { header: span.textContent.trim(), index };
}
}
return map;
}, {});
// Extract data rows
const dataRows = Array.from(document.querySelectorAll(".ListItemView"));
const data = dataRows.map((row) => {
const rowData = {};
const fixedColumns = Array.from(
row.querySelectorAll(".ListItemView_fixedExtra .ListItemView_commonBox")
);
// Extract fixed columns based on order
fixedColumns.forEach((box, index) => {
const span = box.querySelector("span");
rowData[`fixed_${index}`] = span ? span.textContent.trim() : "";
});
// Extract other columns
Array.from(row.querySelectorAll(".ListItemView_commonBox"))
.filter((box) => !box.closest(".ListItemView_detailExtra"))
.forEach((box) => {
const className = Array.from(box.classList).find((cls) =>
/^[a-z]+$/.test(cls)
); // Find the class name matching the pattern
if (className) {
const span = box.querySelector("span");
if (className === "ccts") {
// Special case for 持仓天数
const value = span ? span.textContent.trim() : "";
rowData[className] = value + " 天";
} else {
rowData[className] = span ? span.textContent.trim() : "";
}
}
});
return rowData;
});
// Reorder data rows to match the header order
const headers = Object.values(headerMap).map((item) => item.header);
const orderedData = data.map((row) =>
Object.keys(headerMap).map((key, index) => {
if (row[key] !== undefined) {
return row[key];
}
// Fallback to fixed column if no match
return row[`fixed_${index}`] || "";
})
);
// Convert to CSV
const csvContent = [headers, ...orderedData]
.map((row) => row.map((cell) => `"${cell.replace(/"/g, '""')}"`).join(","))
.join("\n");
// Download CSV
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "investment_data.csv";
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment